四叉树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->iId;
				if(pMarkerObj->以上是关于四叉树lod结合灯塔AOI的主要内容,如果未能解决你的问题,请参考以下文章

回想四叉树LOD地形(上)

回想四叉树LOD地形(上)

关爱青少年作品,灯塔

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

哲元科技×飞桨EasyDL|助力世界500强企业打造“灯塔工厂”,探索智能制造星辰大海

在灯塔工厂点亮5G,宁德时代抢先探路中国智造