深度探索DxFramework 10-3

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com

第10章 进入三维的世界 3

10.5 无规矩不成方圆,C_BoundingBox类

10.5.1.包围盒的种类

DXFrameWork的包围盒是一个凸六面体,由6个面,8个顶点,12条边组成的闭合体。因此,DXFrameWork也使用了相关的数据结构来描述包围盒。代码如下:

1class C_BoundingBox
2{
3    ...
4public:
5    D3DXVECTOR3 point[8]; //8个顶点
6    D3DXPLANE     plane[6]; //6个表面
7}

8个顶点和6个面和编号的示意图如下:

###10.5.2.包围盒的数据结构和初始化设置

  1//根据给定的图元计算出它的包围盒
  2void C_BoundingBox::FindBoundingBox(C_Primitive* p_Primitive) 
  3{
  4    unsigned i;
  5
  6    //获取
  7    DWORD numVertices = p_Primitive->numVertices;
  8
  9    // Lock the vertex buffer
 10    C_VertexManipulator vertexMemory;
 11    p_Primitive->LockVertexBuffer(&vertexMemory);
 12
 13    // Find the mins and maxs for each X Y and Z axis
 14    D3DXVECTOR3 position;
 15
 16    float minX;
 17    float minY;
 18    float minZ;
 19    float maxX;
 20    float maxY;
 21    float maxZ;
 22
 23    position = vertexMemory[0].position;
 24    minX = maxX = position.x;
 25    minY = maxY = position.y;
 26    minZ = maxZ = position.z;
 27
 28    //遍历每一个顶点,找出X,Y,Z值最大和最小值
 29    for (i = 1; i < numVertices; i++) {
 30        position = vertexMemory[i].position;
 31        if (position.x < minX) {
 32            minX = position.x;
 33        }
 34        if (position.y < minY) {
 35            minY = position.y;
 36        }
 37        if (position.z < minZ) {
 38            minZ = position.z;
 39        }
 40        if (position.x > maxX) {
 41            maxX = position.x;
 42        }
 43        if (position.y > maxY) {
 44            maxY = position.y;
 45        }
 46        if (position.z > maxZ) {
 47            maxZ = position.z;
 48        }
 49    }
 50
 51    // Unlock the vertex buffer
 52    p_Primitive->UnlockVertexBuffer();
 53
 54    //如果一些图元的包围盒是不是三维的话,比如,一个二维面片的包围盒。
 55    //那么,就要适当地调整一下,使得包围盒能变成三维的
 56    if (fabs(minX - maxX) < 0.01f) { 
 57        minX -= 0.1f;
 58        maxX += 0.1f; 
 59    }
 60
 61    if (fabs(minY - maxY) < 0.01f) { 
 62        minY -= 0.1f;
 63        maxY += 0.1f; 
 64    }
 65
 66    if (fabs(minZ - maxZ) < 0.01f) { 
 67        minZ -= 0.1f;
 68        maxZ += 0.1f; 
 69    }
 70
 71    //创建组成三角形的八个顶点,从0到7的顺序,X,Y,Z值分别是:
 72    //0 右,下,近
 73    //1 右,上,近
 74    //2 左,上,近
 75    //3 右,上,近
 76    //4 右,下,远
 77    //5 右,上,远
 78    //6 左,上,远
 79    //7 左,下,远
 80    // 意思是,前4个
 81    point[0] = D3DXVECTOR3(maxX, minY, minZ); // Right Bottom Near
 82    point[1] = D3DXVECTOR3(maxX, maxY, minZ); // Right Top Near
 83    point[2] = D3DXVECTOR3(minX, maxY, minZ); // Left Top Near
 84    point[3] = D3DXVECTOR3(minX, minY, minZ); // Right Top Near
 85    point[4] = D3DXVECTOR3(maxX, minY, maxZ); // Right Bottom Far
 86    point[5] = D3DXVECTOR3(maxX, maxY, maxZ); // Right Top Far
 87    point[6] = D3DXVECTOR3(minX, maxY, maxZ); // Left Top Far
 88    point[7] = D3DXVECTOR3(minX, minY, maxZ); // Left Bottom Far
 89
 90    // Obtain the 6 plane specifying the bounding box
 91    D3DXPlaneFromPoints(&plane[0], &point[2], &point[1], &point[0]); //Near
 92    D3DXPlaneFromPoints(&plane[1], &point[5], &point[6], &point[4]); //Far
 93    D3DXPlaneFromPoints(&plane[2], &point[7], &point[6], &point[2]); //Left
 94    D3DXPlaneFromPoints(&plane[3], &point[1], &point[5], &point[0]); //Right
 95    D3DXPlaneFromPoints(&plane[4], &point[4], &point[3], &point[0]); //Bottom
 96    D3DXPlaneFromPoints(&plane[5], &point[2], &point[5], &point[1]); //Top
 97
 98    // Validate the bounding box
 99    valid = true;
100}
 1//根据指定的包围盒的大小范围查找到对应的包围盒
 2void C_BoundingBox::FindBoundingBox(float minX, float minY, float minZ, float maxX, float maxY, float maxZ) {
 3
 4    /* 当有些包围盒不是3D的话,比如其中有一维的值为0,则调整它变成真正三维的盒子*/
 5    if (fabs(minX - maxX) < 0.01f) { 
 6        minX -= 0.1f;
 7        maxX += 0.1f; 
 8    }
 9
10    if (fabs(minY - maxY) < 0.01f) { 
11        minY -= 0.1f;
12        maxY += 0.1f; 
13    }
14
15    if (fabs(minZ - maxZ) < 0.01f) { 
16        minZ -= 0.1f;
17        maxZ += 0.1f; 
18    }
19
20    point[0] = D3DXVECTOR3(maxX, minY, minZ); // Right Bottom Near
21    point[1] = D3DXVECTOR3(maxX, maxY, minZ); // Right Top Near
22    point[2] = D3DXVECTOR3(minX, maxY, minZ); // Left Top Near
23    point[3] = D3DXVECTOR3(minX, minY, minZ); // Right Top Near
24    point[4] = D3DXVECTOR3(maxX, minY, maxZ); // Right Bottom Far
25    point[5] = D3DXVECTOR3(maxX, maxY, maxZ); // Right Top Far
26    point[6] = D3DXVECTOR3(minX, maxY, maxZ); // Left Top Far
27    point[7] = D3DXVECTOR3(minX, minY, maxZ); // Left Bottom Far
28
29    // Obtain the 6 plane specifying the bounding box
30    D3DXPlaneFromPoints(&plane[0], &point[2], &point[1], &point[0]); //Near
31    D3DXPlaneFromPoints(&plane[1], &point[5], &point[6], &point[4]); //Far
32    D3DXPlaneFromPoints(&plane[2], &point[7], &point[6], &point[2]); //Left
33    D3DXPlaneFromPoints(&plane[3], &point[1], &point[5], &point[0]); //Right
34    D3DXPlaneFromPoints(&plane[4], &point[4], &point[3], &point[0]); //Bottom
35    D3DXPlaneFromPoints(&plane[5], &point[2], &point[5], &point[1]); //Top
36
37    // Validate the bounding box
38    valid = true;
39}
40 
41void C_BoundingBox::SetBoundingBox(D3DXVECTOR3 point[8]) {     int i;
42
43    for (i = 0; i < 8; i++) {
44        this->point[i]=point[i];
45    }
46
47    // Obtain the 6 plane specifying the bounding box
48    D3DXPlaneFromPoints(&plane[0], &point[2], &point[1], &point[0]); //Near
49    D3DXPlaneFromPoints(&plane[1], &point[5], &point[6], &point[4]); //Far
50    D3DXPlaneFromPoints(&plane[2], &point[7], &point[6], &point[2]); //Left
51    D3DXPlaneFromPoints(&plane[3], &point[1], &point[5], &point[0]); //Right
52    D3DXPlaneFromPoints(&plane[4], &point[4], &point[3], &point[0]); //Bottom
53    D3DXPlaneFromPoints(&plane[5], &point[2], &point[5], &point[1]); //Top
54
55    // 确认包围盒有效
56    valid = true;
57}
58 
59void C_BoundingBox::SetBoundingBox(D3DXVECTOR3 point[8], D3DXPLANE plane[6])
60{
61    int i;
62
63    for (i = 0; i < 8; i++) {
64        this->point[i] = point[i];
65    }
66
67    for (i = 0; i < 6; i++) {
68        this->plane[i] = plane[i];
69    }
70
71    valid = true;
72}

10.5.3.包围盒的相交种类和相交判定

在dxframework中,包围盒的相交种类分为四种,分别是:相离,相交,覆盖,被包含四种。

相离的情况如下,所谓相离是指,A包围盒和B包围盒之间,A包围盒中任意一个顶点,都不在B包围盒内部。或者B包围盒中任意一个顶点,都不在A包围盒的内部。示意图如下:

相交是指:B包围盒的一部分的顶点在A包围盒的内部,这时候称为A包围盒与B包围盒相交。示意图如下:

覆盖和被包含是指:B包围盒的全部顶点完全在A包围盒内部。这时候称为A包围盒“覆盖”了B包围盒。或者称为:B包围盒“被包含”于A包围盒。示意图如下:

C_BoundingBox类提供了包围盒相交检测的函数,如下:

  1//核心函数,判断本包围盒是否和p_BoundingBox包围盒相交
  2INTERSECTION_TYPE C_BoundingBox::IsIntersecting( C_BoundingBox *p_BoundingBox) 
  3{
  4
  5    if ((p_BoundingBox == NULL) || (!p_BoundingBox->valid) || (!this->valid)) 
  6    {
  7        //两包围盒必须要有效,即各顶点信息和表明信息要计算好才能进行相交判断
  8        throw Error(_T("C_BoundingBox::IsIntersecting : Bounding box(es) do not exist or are not valid"));
  9    }
 10
 11    //注释约定:first box指本包围盒(this指针所指向的包围盒对象),
 12    //second box指传递进来的参数指针所指向的包围盒。
 13    int i,j;
 14
 15     //out[i]数据中的第k位表示是否第i个包围盒顶点是否在第k个平面的外测
 16    DWORD out[8]; 
 17
 18    //检查second box的顶点是否在first box里面
 19    for (i = 0; i < 8; i++)    {
 20        out[i] = 0;
 21    }
 22    
 23    bool aPointInside = false;
 24    int sumOut = 0;
 25
 26    for (i = 0; i < 8; i++) 
 27    {
 28        for (j = 0; j < 6; j++)
 29        {
 30            //调用工具函数,检查第i个second box是否在
 31            //first box的第j个表面正则。
 32            if (positiveSide(&p_BoundingBox->point[i], 
 33                             &plane[j]))
 34            {
 35                out[i] |=(1 << j);
 36            }
 37        }
 38
 39        //如果第i个second box的顶点都在所有的first box的的负侧
 40        //表明表示这个第i个second box的顶点在first box的内部。
 41        if (!out[i]) 
 42        {
 43            aPointInside = true;
 44        }
 45        sumOut += out[i];
 46    }
 47
 48    //如果second box至少有一个点在first box的内侧
 49    if (aPointInside) 
 50    {
 51        // sumOut等于0。表示所有的second box的顶点都在first box
 52        // 内部。所以first box覆盖second box
 53        if (sumOut == 0) 
 54        {
 55            return COMPLETELY_COVER;
 56        }
 57        return PARTIALY_INTERSECTING;
 58    }
 59
 60    D3DXVECTOR3 intersectionPoint;
 61    if ((((((((out[0] & out[1]) & out[2]) & out[3]) 
 62        & out[4]) & out[5]) & out[6]) & out[7]) != 0) 
 63    {
 64        // 所有的顶点都在某一个面的正测,表示这两个包围盒是相离的
 65        return NOT_INTERSECTING;
 66    }
 67
 68    //检查first box的某一边是否穿过second box
 69    {
 70        D3DXVECTOR3 edges[12][2] = {
 71        p_BoundingBox->point[0], p_BoundingBox->point[1],
 72        p_BoundingBox->point[1], p_BoundingBox->point[2],
 73        p_BoundingBox->point[2], p_BoundingBox->point[3],
 74        p_BoundingBox->point[3], p_BoundingBox->point[0],
 75        p_BoundingBox->point[0], p_BoundingBox->point[4],
 76        p_BoundingBox->point[1], p_BoundingBox->point[5],
 77        p_BoundingBox->point[2], p_BoundingBox->point[6],
 78        p_BoundingBox->point[3], p_BoundingBox->point[7],
 79        p_BoundingBox->point[4], p_BoundingBox->point[5],
 80        p_BoundingBox->point[5], p_BoundingBox->point[6],
 81        p_BoundingBox->point[6], p_BoundingBox->point[7],
 82        p_BoundingBox->point[7], p_BoundingBox->point[4]};
 83
 84        D3DXVECTOR3 faces[6][4] = {
 85        point[3], point[2], point[1], point[0],//近Z,XY面
 86        point[5], point[6], point[7], point[4],//远Z,XY面
 87        point[3], point[7], point[6], point[2],//左X,YZ面
 88        point[1], point[5], point[4], point[0],//右X,YZ面    
 89        point[4], point[7], point[3], point[0],//底Y,XZ面
 90        point[2], point[6], point[5], point[1]};//顶Y,XZ面
 91
 92        for (i = 0; i < 12; i++) 
 93        {
 94            for (j = 0; j < 6; j++)
 95            {
 96                //调用工具函数checkEdgeIntersects检查第i条first 
 97                //box的边是否穿过第j个second box的面
 98                if(checkEdgeIntersectsFace(edges[i],faces[j], 
 99                    4, &plane[j], &intersectionPoint)) 
100                    { 
101                        return PARTIALY_INTERSECTING;
102                    }
103                }
104            }
105    }
106
107    //检查second box的某一边是否穿过first box
108    {
109        D3DXVECTOR3 edges[12][2] = {
110                    point[0], point[1],
111                    point[1], point[2],
112                    point[2], point[3],
113                    point[3], point[0],
114                    point[0], point[4],
115                    point[1], point[5],
116                    point[2], point[6],
117                    point[3], point[7],
118                    point[4], point[5],
119                    point[5], point[6],
120                    point[6], point[7],
121                    point[7], point[4]};
122
123        D3DXVECTOR3 faces[6][4] = {
124        p_BoundingBox->point[3], p_BoundingBox->point[2], 
125        p_BoundingBox->point[1], p_BoundingBox->point[0],    
126        p_BoundingBox->point[5], p_BoundingBox->point[6], 
127        p_BoundingBox->point[7], p_BoundingBox->point[4],     
128        p_BoundingBox->point[3], p_BoundingBox->point[7], 
129        p_BoundingBox->point[6], p_BoundingBox->point[2],    
130        p_BoundingBox->point[1], p_BoundingBox->point[5], 
131        p_BoundingBox->point[4], p_BoundingBox->point[0],      
132        p_BoundingBox->point[4], p_BoundingBox->point[7], 
133        p_BoundingBox->point[3], p_BoundingBox->point[0],    
134        p_BoundingBox->point[2], p_BoundingBox->point[6], 
135        p_BoundingBox->point[5], p_BoundingBox->point[1]};   
136
137        for (i = 0; i < 12; i++) 
138        {
139            for (j = 0; j < 6; j++)
140            {
141                if(checkEdgeIntersectsFace(edges[i],faces[j],
142                                4, &p_BoundingBox->plane[j], 
143                                &intersectionPoint)) 
144                {
145                      return PARTIALY_INTERSECTING;
146                }
147            }
148        }
149    }
150
151    //检查一下是否first box的一点是否至少在second box的一个面的正侧,
152    //如果是的话,表示两盒子相离。如果否的话,在上述的各种情况都检查过的
153    //前提下,就表示,first box在second box的内部,即first box被
154    //包含于second box
155    for (i = 0; i < 6; i++)
156    {
157        if( positiveSide(&point[0], 
158            &p_BoundingBox->plane[i])) 
159        {
160            return NOT_INTERSECTING; 
161        }
162    }
163
164    return COMPLETELY_INSIDE;
165}

上面的函数检查两包围盒相交与否是非常精确的。但是也可以看到,检测的计算非常耗时。尤其是后面判断first box是否被包含于second box的部分。如果我们能够预先知道first box的体积肯定大于second box的话。那么后面的“被包含”判断就可以省略的。因此,DXFrameWork提供了一个比较快速的相交判断函数:INTERSECTION_TYPE C_BoundingBox:: LazyIsIntersecting (C_BoundingBox *p_BoundingBox)。和IsIntersectiong函数相比,LazyIsIntersecting函数省略了检查“被包含”的判断。因此使用此函数的时候,要保证first box的体积大于second box才能得到正确的判断。

除了判断包围盒之间的相交检测之外,C_BoundingBox类还提供了包围盒和线段之间的相交判断函数,如下:

 1bool C_BoundingBox::IsPenetrated(D3DXVECTOR3 p_Edge[2]) 
 2{
 3    int j;
 4    D3DXVECTOR3 intersectionPoint;
 5    D3DXVECTOR3 faces[6][4] = {
 6        point[3], point[2], point[1], point[0],       //Near
 7        point[5], point[6], point[7], point[4],       //Far
 8        point[3], point[7], point[6], point[2],       //Left
 9        point[1], point[5], point[4], point[0],       //Right    
10        point[4], point[7], point[3], point[0],       //Bottom
11        point[2], point[6], point[5], point[1]};      //Top
12
13    for (j = 0; j < 6; j++) 
14    {
15        if (checkEdgeIntersectsFace(p_Edge, faces[j], 4,&plane[j], &intersectionPoint)) 
16        {
17              return true;
18
19        }
20    }
21    return false;
22}

从上述代码看出,要检查某线段是否和某包围盒相交,就只是需要检查某线段是否和包围盒的任意一个表面相交即可。如果是则表示这线段和包围盒相交,否就表示不相交。