四叉树lod结合灯塔AOI

Posted ₂₁₃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四叉树lod结合灯塔AOI相关的知识,希望对你有一定的参考价值。

一些字段的解释

  1. 观察者:我可以观察到那些人。
  2. 被观察者:那些人能观察到自己。
  3. #define WATCHER_MODE 0x01 观察者模式
  4. #define MARKER_MODE 0x02 被观察者模式

灯塔相关结构体

1:灯塔区域结构

struct towerSpace_s
{
	void (*callback)(void*pUserData,bool bAddTo,uint64_t watcher, uint64_t marker); -- 回调函数
	void*			pUserData; //用户信息
	float			fMin[2]; //最小位置
	float			fGridLength[3];//网格x y,z方向长度
	float			fMovefRange;//移动范围
	int32_t			iSplitThreshold;//拆分阈值
	int32_t 		iMaxWidth; //最大宽度
	int32_t 		iMaxHeight;//最大高度
    int32_t*  		pGrids;//网格数据
	tower_tt*		pTowers;//灯塔数据
	int32_t			iTowerNext;//下一个灯塔id
	int32_t			iTowerCapacity;//灯塔容量
	aoiObj_tt* 		pSlotObj;//格子里面对象
	int32_t			iSlotIndex;//格子索引
	int32_t     	iSlotCapacity;//格子容量
};

2:灯塔信息结构

typedef struct tower_s
{
	aoi_tree_tt watcher; //灯塔观察者[用来存储观察到的对象]
	aoi_tree_tt	marker;//灯塔被观察者
	int32_t     iMarkerCount;//被观察者数量
	int32_t  	iFirstChildId;//第一个儿子节点索引
} tower_tt;

3:灯塔划分后的节点结构【四个儿子节点】

typedef struct aoiNode_s
{
	RB_ENTRY(aoiNode_s) entry; //实体
	int32_t				iId; //id
} aoiNode_tt;

4:灯塔里面对象结构

typedef struct aoiObj_s
{ 
	int32_t		iId; // id
	int32_t 	iMode; // 模式(MARKER_MODE:被观察者模式 WATCHER_MODE:观察模式)
	uint64_t    uiMask;//掩码
	uint64_t    uiUserData; //用户数据
	float		fViewRadius; // 视野半径
	float 		last[3]; //上一个xyz位置
	float 		pos[3];//当前xyz位置
} aoiObj_tt;

灯塔AOI相关的一些操作函数

int32_t luaopen_laoi(lua_State *L)
{
#ifdef luaL_checkversion
	luaL_checkversion(L);
#endif
	registerTowerSpaceL(L);

	luaL_Reg lualib_funcs[] =
	{
		{"createAoiSpace",		lcreateAoiSpace},//创建aoi区域
		{NULL, NULL}
	};
	luaL_newlib(L, lualib_funcs);
	return 1;
}

int32_t registerTowerSpaceL(struct lua_State *L)
{
	luaL_newmetatable(L, "towerSpace");
	lua_pushvalue(L, -1);
	lua_setfield(L, -2, "__index");

	struct luaL_Reg lua_towerSpaceFuncs[] = 
	{
		{"setCallback",		laoi_setCallback}, //设置回调函数
		{"addObj",			laoi_addObj},//增加一个实体对象
		{"removeObj",		laoi_removeObj},//移除一个实体对象
		{"updateObjMask",	laoi_updateObjMask},//更新对象的mask[0x01:观察者 0x02:被观察者]
		{"updateObjPos",	laoi_updateObjPos},//更新对象的pos
		{"addObjWatcher",	laoi_addObjWatcher},//增加对象到相应的观察容器
		{"removeObjWatcher",laoi_removeObjWatcher},//从相应的观察容器移除对象
		{"addObjMarker",	laoi_addObjMarker},//增加对象到被观察者
		{"removeObjMarker",	laoi_removeObjMarker},//移除对象到被观察者
		{"__gc", 			laoi_towerSpace_gc},//此区域进行GC回收
		{NULL, NULL}
	};

	luaL_setfuncs(L, lua_towerSpaceFuncs, 0);
	return 1;
}

四叉树lod示意图在这里插入图片描述

  1. 黑色大框是AOI的区域大小
  2. 每个正方形块上面都有一个灯塔
  3. 暂时定的最多分裂3层
  4. 黑色的原点是场景内的实体

灯塔AOI一些关键函数[具体代码太多了有时间上传github]

1. 更新观察者集合

inline static void changeAoiObjWatcher(towerSpace_tt* pTowerSpace,aoiObj_tt* pObj)
{
	float bmin[3];
	float bmax[3];

	bmin[0] = pObj->last[0] - pObj->fViewRadius;
	bmin[2] = pObj->last[2] - pObj->fViewRadius;
	bmax[0] = pObj->last[0] + pObj->fViewRadius;
	bmax[2] = pObj->last[2] + pObj->fViewRadius;
	
	int32_t minxLast = 0;
	int32_t minyLast = 0;
	int32_t maxxLast = 0;
	int32_t maxyLast = 0;

	calcGridLodLoc(pTowerSpace, 2,bmin, &minxLast, &minyLast);
	calcGridLodLoc(pTowerSpace, 2,bmax, &maxxLast, &maxyLast);

	minxLast = minxLast > 0 ? minxLast : 0;
	minyLast = minyLast > 0 ? minyLast : 0;
	maxxLast = maxxLast < pTowerSpace->iMaxWidth * 4 ? maxxLast : pTowerSpace->iMaxWidth*4 - 1;
	maxyLast = maxyLast < pTowerSpace->iMaxHeight * 4 ? maxyLast : pTowerSpace->iMaxHeight*4 - 1;


	bmin[0] = pObj->pos[0] - pObj->fViewRadius;
	bmin[2] = pObj->pos[2] - pObj->fViewRadius;

	bmax[0] = pObj->pos[0] + pObj->fViewRadius;
	bmax[2] = pObj->pos[2] + pObj->fViewRadius;

	int32_t minx = 0;
	int32_t miny = 0;
	int32_t maxx = 0;
	int32_t maxy = 0;

	calcGridLodLoc(pTowerSpace, 2,bmin, &minx, &miny);
	calcGridLodLoc(pTowerSpace, 2,bmax, &maxx, &maxy);

	minx = minx > 0 ? minx : 0;
	miny = miny > 0 ? miny : 0;
	maxx = maxx < pTowerSpace->iMaxWidth*4 ? maxx : pTowerSpace->iMaxWidth*4 - 1;
	maxy = maxy < pTowerSpace->iMaxHeight*4 ? maxy : pTowerSpace->iMaxHeight*4 - 1;
	
	//是否重合
	if(isOverlap(minx,miny,maxx,maxy,minxLast,minyLast,maxxLast,maxyLast))
	{
		int32_t iMinX = minx < minxLast ? minx : minxLast;
		int32_t iMinY = miny < minyLast ? miny : minyLast;
		int32_t iMaxX = maxx >= maxxLast ? maxx : maxxLast;
		int32_t iMaxY = maxy >= maxyLast ? maxy : maxyLast;

		int32_t iChanged = 0;

		//往上找到最大的网格块
		//为什么是iMinY/4 是因为除以4就像四叉树一样找最上面的父节点的索引值一样
		for (int32_t iY = iMinY/4; iY < (iMaxY+3)/4; ++iY) 
		{
			for (int32_t iX = iMinX/4; iX < (iMaxX+3)/4; ++iX)
			{
				iChanged = 0;
				if(isInInside(iX*4,iY*4,iX*4+3,iY*4+3,minxLast,minyLast,maxxLast,maxyLast))
				{
					iChanged = 0x1;
				}

				if(isInInside(iX*4,iY*4,iX*4+3,iY*4+3,minx,miny,maxx,maxy))
				{
					iChanged |= 0x2;
				}

				switch (iChanged)
				{
				case 0x1:
					{
						removeGridWatcher(pTowerSpace,pObj,iX,iY);
					}
					break;
				case 0x2:
					{
						insertGridWatcher(pTowerSpace,pObj,iX,iY,pObj->pos);
					}
					break;
				case 0x3:
					{
						int32_t iTowerId = pTowerSpace->pGrids[iX + iY * pTowerSpace->iMaxWidth];
						assert(iTowerId != -1);
						tower_tt* pTower = pTowerSpace->pTowers + iTowerId;
						if (pTower->iFirstChildId == -1)
						{
							continue;
						}

						for (int32_t ly = 0; ly < 2; ly++)
						{
							for (int32_t lx = 0; lx < 2; lx++)
							{
								iChanged = 0;
								if (isInInside(iX * 4 + lx * 2, iY * 4 + ly * 2, iX * 4 + lx * 2 + 1, iY * 4 + ly * 2 + 1, minxLast, minyLast, maxxLast, maxyLast))
								{
									iChanged = 0x1;
								}

								if (isInInside(iX * 4 + lx * 2, iY * 4 + ly * 2, iX * 4 + lx * 2 + 1, iY * 4 + ly * 2 + 1, minx, miny, maxx, maxy))
								{
									iChanged |= 0x2;
								}

								switch (iChanged)
								{
								case 0x1:
									{
										removeLodWatcher(pTowerSpace, iTowerId, pObj, iX, iY, iX * 4 + lx * 2, iY * 4 + ly * 2);
									}
									break;
								case 0x2:
									{
										insertLodWatcher(pTowerSpace, iTowerId, pObj, iX, iY, iX * 4 + lx * 2, iY * 4 + ly * 2);
									}
									break;
								case 0x3:
									{
										tower_tt* pLodTower = pTowerSpace->pTowers + pTower->iFirstChildId + ly * 2 + lx;
										if (pLodTower->iFirstChildId == -1)
										{
											continue;
										}

										for (int32_t l2y = 0; l2y < 2; l2y++)
										{
											for (int32_t l2x = 0; l2x < 2; l2x++)
											{
												iChanged = 0;
												if (isInRect(iX * 4 + lx * 2+l2x, iY * 4 + ly * 2+l2y,  minxLast, minyLast, maxxLast, maxyLast))
												{
													iChanged = 0x1;
												}

												if (isInRect(iX * 4 + lx * 2+l2x, iY * 4 + ly * 2+l2y, minx, miny, maxx, maxy))
												{
													iChanged |= 0x2;
												}

												switch (iChanged)
												{
												case 0x1:
													{
														tower_tt* pLod2Tower = pTowerSpace->pTowers + pLodTower->iFirstChildId + l2y * 2 + l2x;
														aoiNode_tt findNode;
														findNode.iId = pObj->iId;
														aoiNode_tt* pT = RB_FIND(aoi_tree_s, &pLod2Tower->watcher, &findNode);
														assert(pT);
														RB_REMOVE(aoi_tree_s, &pLod2Tower->watcher, pT);
														mem_free(pT);

														aoiNode_tt* pI;
														RB_FOREACH(pI, aoi_tree_s, &pLod2Tower->marker)
														{
															aoiObj_tt* pMarkerObj = pTowerSpace->pSlotObj + pI->iId;
															if ((pI->iId != pObj->iId) && (pObj->uiMask & pMarkerObj->uiMask))
															{
																pTowerSpace->callback(pTowerSpace->pUserData, false, pObj->uiUserData, pMarkerObj->uiUserData);
															}
														}
													}
													break;
												case 0x2:
													{
														tower_tt* pLod2Tower = pTowerSpace->pTowers + pLodTower->iFirstChildId + l2y * 2 + l2x;
														aoiNode_tt* pNode = mem_malloc(sizeof(aoiNode_tt));
														pNode->iId = pObj->iId;
														RB_INSERT(aoi_tree_s, &pLod2Tower->watcher, pNode);

														aoiNode_tt* pI;
														RB_FOREACH(pI, aoi_tree_s, &pLod2Tower->marker)
														{
															aoiObj_tt* pMarkerObj = pTowerSpace->pSlotObj + pI->iId;
															if ((pI->iId != pObj->iId) && (pObj->uiMask & pMarkerObj->uiMask))
															{
																pTowerSpace->callback(pTowerSpace->pUserData, true, pObj->uiUserData, pMarkerObj->uiUserData);
															}
														}
													}
													break;
												}
											}
										}
									}
									break;
								}
							}
						}
					}
					break;
				}
			}
		}
	}
	else
	{
		for (int32_t iY = minyLast / 4; iY <= (maxyLast + 3) / 4; ++iY)
		{
			for (int32_t iX = minxLast / 4; iX <= (maxxLast + 3) / 4; ++iX)
			{
				removeGridWatcher(pTowerSpace, pObj, iX, iY);
			}
		}

		for (int32_t iY = miny/4; iY <= (maxy+3)/4; ++iY)
		{
			for (int32_t iX = minx/4; iX <= (maxx+3)/4; ++iX)
			{
				insertGridWatcher(pTowerSpace,pObj,iX,iY,pObj->pos);
			}
		}
	}	
}

2. 把被观察者转入观察者容器

inline static void changeAoiObjMaskToWatcher(towerSpace_tt* pTowerSpace,aoiObj_tt* pObj,int32_t iX,int32_t iY,uint64_t uiMask)
{
	int32_t iTowerId = pTowerSpace->pGrids[iX+iY*pTowerSpace->iMaxWidth];
    assert(iTowerId != -1);
    
	tower_tt* pTower = pTowerSpace->pTowers + iTowerId;
    if (pTower->iFirstChildId == -1)
    {
		int32_t iChanged;
		aoiNode_tt* pI; 
		RB_FOREACH(pI,aoi_tree_s,&pTower->marker)
		{
			if(pI->iId != pObj->iId)
			{
				iChanged = 0;
				aoiObj_tt* pMarkerObj = pTowerSpace->pSlotObj + pI回想四叉树LOD地形(上)

回想四叉树LOD地形(上)

opengl多层次细节LOD四叉树网格无限动态绘制

Unity 模型的动态LOD优化

四叉树与碰撞检测 !Cocos Creator !

chunked lod pros and cons