空间划分(real time collision detection chapter 7)

来源:互联网 发布:淘宝在线客服在哪里 编辑:程序博客网 时间:2024/06/01 08:52

本章介绍三种空间划分方法:均匀网格划分、分层网格划分、树划分


7.1 均匀网格划分


7.1.1 链表存储

定义一个数组,然后将网格单元(grid cell)与数组元素一一对应。当一个单元中有对象(object)时,数组元素就是一个链表,保存在该单元内的所有对象,否则指NULL。

最简单维持对象链表的办法是,在对象类型(结构体)中包含链表成员变量。该方法既可以提高数据缓存并发又能避免链表声明而简化代码

由于每个数组元素都是一个链表(密集数组),对于具有大量单元的网格,其每个单元对应的(数组)元素都存储一个链表(表头),也需要很大的存储开销。而为此通过增大单个单元的尺寸以减少单元数量也不好,因为当对象呈聚集状时(而且是非常重要的应用情形),这样粗糙的划分没有了意义。后续几节给出几种低存储要求的可行办法。


7.1.2 哈希存储

上述链表存储的问题可由哈希方法解决。将网格单元映射到桶数目固定的哈希表。指定单元所含的对象链表就存在于某个桶里。下面给了一个简单的乘法哈希函数,其将单元坐标位置与桶索引对应:

struct Cell {  Cell(int32 px, int32 py, int32 pz) {x=px; y=py; z=pz;}  int32 x, y, z;}#define NUM_BUCKETS 1024// computes hash bucket index in range [0, NUM_BUCKETS-1]int32 ComputeHashBucketIndex(Cell cellPos) {  const int32 h1=0x8da6b343; // large multiplicative constants  const int32 h2=0xd8163841; // here arbitrarialy chosen primes  const int32 h3=0xcb1ab31f;  int32 n=h1*cellPos.x+h2*cellPos.y+h3*cellPos.z;  n=n%NUM_BUCKETS;  IF(n<0) n+=NUM_BUCKETS return n; }


哈希存储有个好处是单元数目不做限制。映射函数将全局坐标封装到有限个单元里(这样为抵达邻域而进行的边界检测也不必要了)。虽然全局范围内可能包含无限多单元,但每个给定时刻都对象只“占据”(overlapped)有限数目的单元。即散列存储仅与对象的数目相关而与网格单元数目无关。

为避免哈希表查找空的网格单元,再使用一个密集位数组来快速检测某个单元是否为空。当然,这一优化仅当单元尺寸固定时可用。

解决哈希表冲突问题,常见开散列、闭散列方法,具体参考哈希相关资料。


7.1.3 静态数据存储

静态网格数据,比如多边形单元数据,存储不需要链表,而采用储连续数组。下图阐释链表存储和数组存储:


单元数据(即单元内对象个数)分配到数组可分为两个传入步骤。首先,遍历所有对象数据:当某个对象落入某个单元,并不直接将该对象数据加入该单元,而仅对该单元的对象计数器进行加1操作。当所有对象走完一遍后,每个单元的计数器就知道了各个单元Ck里有ak个对象。这样共需分配的数组大小m:

         M=sum(ak) k=0,n

每个单元还有一个全局索引号B(k):

         B(0)=0

         B(k)=b(k-1)+a(k-1)

这时就进行第二遍遍历。对含有a(k)个对象的单元c(k),对象编号采用全局索引就表示成b(k)到b(k)+a(k)-1。因为每个单元中的数据都是连续存储在数组中的,这样基于数组存储的网格单元使用了更少的内存,相比链表存储具有更高的cache效率。


7.1.4 隐式存储

间接地采用笛卡尔坐标形式。对平面网格使用两个数组(3D需要三个数组),其中一个记录网格的行信息,另一个记录网格的列信息。同链表存储,数组的每个元素都是对象链表,存储了所在行或列"具有“(overlapped by)哪些对象。当一个对象”占据“(overlap)某些单元时,需要根据其”占据“单元的位置将该对象分别加入行、列数组对应的位置。这样两对象是否重叠/碰撞就只需检测其所”占据“的行、列是否有重叠。


与链表存储相比,该策略可能造成更少或更多的链表插入操作。当对象完全"落入"某一个单元,需要两次插入操作,即分别插入行链、列链。但在链表存储中只需要一次插入操作。但当对象“占据”,比如4*4个单元,它共需对8个数组元素进行插入操作而链表存储就需要16次插入操作。可见,该方法较适合对象尺寸大于单元尺寸的情形。

另一种新意的存储方法是为网格每行每列分配一个位数组,一个对象就用一位来表示。对象插入过程就可以简化为对该对象的位操作。


当且仅当某个单元在行、列位数组中的位值与对象的位值对应,该对象才“占据”该单元。即对象是否“占据”某个单元的测试,简化为行位数组和列位数组的AND位操作。

当对象"占据"多个单元时,可(利用位操作的分配律)如下操作:假设某个对象“占据”行r(i), r(i+1); 列 c(j), c(j+1),即该对象”占据“四个单元 (r(i), c(j)), (r(i),c(j+1)),(r(i+1),c(j+1)), (r(i+1),c(j)).

 B=(r(i) &c(j)) |(r(i+1)&c(j))| (r(i+1) &c(j+1))| (r(i) &c(j+1));

上式进一步简化:

         B=(r(i)|r(i+1)) &(c(j) |c(j+1))

概括如下:首先对所有“重叠”单元的行、列执行OR位操作,然后对OR位操作的结果执行AND位操作即得到结果。代码如下:

//define the two global bit arraysconst int NUM_OBJECTS_DIV_32=(NUM_OBJECTS+31)/32; // Round upint32 rowBitArray[GRID_HEIGHT][NUM_OBJECTS_DIV_32];int32 columnBitArray[GRID_WIDTH][NUM_OBJECTS_DIV_32];void TestObjectAgainstGrid(Object *pObject) {// allocate temporary bit arrays for all objects and clear them  int32 mergedRowArray[NUM_OBJECTS_DIV_32];  int32 mergedColumnArray[NUM_OBJECTS_DIV_32];  memset(mergedRowArray, 0, NUM_OBJECTS_DIV_32*sizeof(int32));  memset(mergedColumnArray, 0, NUM_OBJECTS_DIV_32*sizeof(int32));//  //Compute the extent of grid cells the bounding sphere of A overlaps//test assumes objects have been inserted in all rows/columns overlapped  float ooCellWidth=1.0f/CELL_WIDTH;  int x1=(int) floorf((pObject->x-pObject->radius)*ooCellWidth);  int x2=(int) floorf((pObject->x+pObject->radius)*ooCellWidth);  int y1=(int) floorf((pObject->y-pObject->radius)*ooCellWidth);  int y2=(int) floorf((pObject->y+pObject->radius)*ooCellWidth);  assert(x1>=0 && y1>=0 && x2<GRID_WIDTH && x2<GRID_HEIGHT);//// compute the merged(bitwise or/ and) bit array of all overlapped grid rows// ditto for all overlapped grid columns  for (int y=y1; y<=y2; y++)for(int i=0; i<NUM_OBJECTS_DIV_32;i++)  mergedRowArray[i] |=rowBitArray[y][i];        for(int x=x1; x<=x2; x++)  for(int i=0; i<NUM_OBJECTS_DIV_32;i++)mergedColumnArray[i] |=ColumnBitArray[y][i];//// now go through the intersection of the merged bit arrays and collision test those // objects having their corresponding bit set   for (int i=0; i<NUM_OBJECTS_DIV_32; i++) { int objectsMask=mergedRowArray[i] & mergedColumnArray[i]; while(objectsMask) {// clear all but lowest bit set (eg. 01101010 -> 00000010)int32 objectsMask=objectsMask &(objectsMaskk-1);// get index number of set bit, test against corresponding object// GetBitIndex(v) returns log_2(v). (i.e. 2^n=v)int32 objectIndex=GetBitIndex(objectMask)+i*32;TestCollisionAgainstObjectNumber(objectIndex);// mask out tested object, and continue with any remaining objectsobjectsMask ^=objectMask; }   }}

网格单元存储的三种方法中隐式位操作最高效。


7.1.5 碰撞测试 /下一篇 next





原创粉丝点击