四叉树lod结合灯塔AOI
Posted ₂₁₃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了四叉树lod结合灯塔AOI相关的知识,希望对你有一定的参考价值。
一些字段的解释
- 观察者:我可以观察到那些人。
- 被观察者:那些人能观察到自己。
- #define WATCHER_MODE 0x01 观察者模式
- #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示意图
- 黑色大框是AOI的区域大小
- 每个正方形块上面都有一个灯塔
- 暂时定的最多分裂3层
- 黑色的原点是场景内的实体
灯塔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地形(上)