本系列博客是由扭曲45原创,欢迎转载,转载时注明出处,http://blog.csdn.net/cg0206/article/details/8257607
随着智能手机的大量普及,手机的性能也越来越好,伴随而来出现大量的手机游戏 ,其中不乏很多优秀的游戏,如《愤怒的小鸟》、《鳄鱼小玩皮》、《翼飞冲天》等等。同时它们也大量的模拟了现实生活中的相关东西,比如小鸟飞行、碰撞的模拟,流体的模拟,都用到了相关的物理知识,他们的开发团队是怎么做到的呢,我们不得而知,但我们可以通过box2d引擎来实现相应的效果。下面我们就来了解并学习下box2d。
一、学习资料
要学习一件事物,首先要知道它是干什么的,有什么作用,以及它的历史,可以通过它的官网http://www.box2d.org获取相应的解答,还可以从http://www.iforce2d.net/b2dtut/introduction网站上面的一系列的教程,以及它的中文版—阿蕉同学的博客【http://blog.csdn.net/wen294299195/article/details/7930907】和http://ohcoder.com/blog/categories/box2d-tutorials/中获取相应的了解,当然你还可以从互联网上搜索相关的知识。
二、介绍
box2d是一个2d刚体仿真物理引擎,也是目前使用最为广泛的,具有很强的移植性,同时它简洁、跨平台、开源、免费,甚至在c++,java,c#,javascript等很多语言上都有实现。我们主要说的是用c++实现的版本。
box2d可以模拟现实世界物体的物理属性,给用户一种真实感,存在感。该引擎是有英国人Erincatto编写的,并作为每天工作的一部分去维护和完善它。现在经常用于游戏框架中的物理引擎部分,像iphone上有名的开源框架cocos2d就是用的此物理引擎,以及最近很火的跨平台引擎cocos2d-x也是用的它作为物理引擎。
三、源码结构
Box2D由三个模块组成:公用(Common),碰撞(Collision)以及动力学(Dynamics)。公用模块包括内存分配,数学库,设置。碰撞模块定义了形状,broad-phase算法,碰撞的功能/查询。最后动力学模块提供了模拟物理世界,物体,定制器(fixtures),以及连接器。下面是三者的关系图。
四、其它
用过cocos2d、或者cocos2d-x的coder应该清楚,其上述两个引擎还提供了另一个物理引擎Chipmunk,下面我们就来比较一下这两者之间的差异:
1、box2d是用c++写的,而Chipmunk用的是c
2、box2d变量和方法使用全称命名,而Chipmunk很多地方使用一个字母的简写
3、box2d使用类【class】,具有很强的封装物体性,而Chipmunk使用结构体【struct】,暴露了过多的细节给外部
4、box2d有针对快速移动直接穿透而不进行碰撞测试的解决方法。而Chipmunk针对object-c有个叫SpaceManager接口,可以很容易的添加精灵到刚体上。
好了,不都说了,相信大家对box2d有了一定了解了。下一篇开始我们正式学习源码。
SOA,全称small object allocator,中文意思是小对象分配器。box2d虽然是用c++写的,但是并没有使用c++自带的new/delete实现内存管理,而是使用在c的malloc/free做法的基础上封装了类b2BlockAllocator进行内存管理,使得分配和使内存变得更加高效、快速。其中b2BlockAllocator就是一个SOA,下面我们就对源码进行分析。
一、b2BlockAllocator类的头文件
首先我们对头文件b2BlockAllocator.h进行大致的了解一遍。不多说,上代码:
[cpp]view plaincopy
1. “font-size: 14px;”<//一次分配内存大小
2. const int32 b2_chunkSize = 16 * 1024;
3. //块子节点大小的最大值
4. const int32 b2_maxBlockSize = 640;
5. //可以申请块子节点大小的类型总数
6. const int32 b2_blockSizes = 14;
7. //块空间增量
8. const int32 b2_chunkArrayIncrement = 128;
9. //块子节点结构体[链表实现]声明
10. struct b2Block;
11. //块结构体声明
12. struct b2Chunk;
13.
14. //这是一个小型的对象分配器,用于一次分配多个小对象
15. class b2BlockAllocator
16. {
17. public:
18. b2BlockAllocator();
19. ~b2BlockAllocator();
20.
21. //分配内存,当size<b2_maxBlockSize则直接用b2Alloc分配
22. void* Allocate(int32 size);
23.
24. //释放内存,当size<b2_maxBlockSize则直接用b2Free释放
25. void Free(void* p, int32 size);
26. //清空内存
27. void Clear();
28.
29. private:
30. //当前块的头指针
31. b2Chunk* m_chunks;
32. //当前已使用的块空间节点总数
33. int32 m_chunkCount;
34. //当前已申请的块空间节点总数
35. int32 m_chunkSpace;
36. //未被使用的内存块链表类型数组,保存了其不同类型链表的头指针
37. b2Block* m_freeLists[b2_blockSizes];
38. //申请的块大小类型数组
39. static int32 s_blockSizes[b2_blockSizes];
40. //根据要申请块的大小获取其类型索引的数组
41. static uint8 s_blockSizeLookup[b2_maxBlockSize + 1];
42. //是否已初始化s_blockSizeLookup数组,标志变量
43. staticbool s_blockSizeLookupInitialized;
44. };
45.
上面文字是对相关字段及方法的注释,我们就不对其进行讲解了。
二、b2BlockAllocator的.c文件
下面我们看该类的具体实现,看b2BlockAllocator.c文件,
1、变量的定义
映入我们眼帘的是一些变量或结构的定义,如下代码:
[cpp]view plaincopy
1. “font-size: 14px;”<int32 b2BlockAllocator::s_blockSizes[b2_blockSizes] =
2. {
3. 16, // 0
4. 32, // 1
5. 64, // 2
6. 96, // 3
7. 128, // 4
8. 160, // 5
9. 192, // 6
10. 224, // 7
11. 256, // 8
12. 320, // 9
13. 384, // 10
14. 448, // 11
15. 512, // 12
16. 640, // 13
17. };
18. uint8 b2BlockAllocator::s_blockSizeLookup[b2_maxBlockSize + 1];
19. bool b2BlockAllocator::s_blockSizeLookupInitialized;
20.
21. struct b2Chunk
22. {
23. int32 blockSize;
24. b2Block* blocks;
25. };
26.
27. struct b2Block
28. {
29. b2Block* next;
30. };
s_blockSizes :是申请的块子节点大小类型数组,主要负责将相应大小的子节点分类;
blockSizeLookup:是根据要申请的子节点size获取s_blockSizes数组的索引,并保持到该数组中;
s_blockSizeLookupInitialized:是否已初始化s_blockSizeLookup数组,标志变量,用于只需要初始化很少次数的变量,可以人为控制【但是最好还是不要那么做】,默认值是false,也许大家感到奇怪,这个变量在哪初始化的,大家可以猜猜看。
b2Chunk是块结构体,其中blockSize表示块子节点大小,blocks表示块头指针;
b2Block表示块子节点结构体,next表示下一个块头指针,如果你感觉到很熟悉的话,那就对了,这是典型的链表定义,将会用链表将子节点链接起来。
2、函数的实现
1)、构造函数和析构函数
接下来就是该类的构造函数和析构函数了,同样我们也看代码。
[cpp]view plaincopy
1. “font-size: 14px;”<b2BlockAllocator::b2BlockAllocator()
2. {
3. b2Assert(b2_blockSizes < UCHAR_MAXspan>
4.
5. m_chunkSpace = b2_chunkArrayIncrement;
6. m_chunkCount = 0;
7. m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
8.
9. memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
10. memset(m_freeLists, 0, sizeof(m_freeLists));
11.
12. if (s_blockSizeLookupInitialized == false)
13. {
14. int32 j = 0;
15. for (int32 i = 1; i < b_maxBlockSizeispan>
16. {
17. b2Assert(j < b_blockSizesspan>
18. if (i < s_blockSizesjspan>
19. {
20. s_blockSizeLookup[i] = (uint8)j;
21. }
22. else
23. {
24. ++j;
25. s_blockSizeLookup[i] = (uint8)j;
26. }
27. }
28.
29. s_blockSizeLookupInitialized = true;
30. }
31. }
32.
33. b2BlockAllocator::~b2BlockAllocator()
34. {
35. for (int32 i = 0; i < m_chunkCountispan>
36. {
37. b2Free(m_chunks[i].blocks);
38. }
39.
40. b2Free(m_chunks);
41. }
在构造函数b2BlockAllocator()中我们初始化相关变量,例如一开始我们就判断b2_blockSizes的有效性,接着为m_chunkSpace、m_chunkCount、m_chuns、m_freeLists、和s_blockSizeLookup的初始化。我们主要说说s_blockSizeLookup的初始化是怎样完成的。
a)、用s_blockSizeLookupInitialized判断s_blockSizeLookup是否已初始化,若没有则进入。大家猜到绿色部分的疑问了没,如果你的答案是编译器,那就恭喜你了。
b)、里面的for循环主要是根据块的大小,将块分类成以上14中类型,并设置索引值,保存到s_blockSizeLookup数组中,j保存的是s_blockSizes数组的索引值。
c)、接着判断j的有效性,这里用的是assert断言,关于assert断言的又不懂的童鞋可以参照维基百科上面的解释http://zh.wikipedia.org/wiki/Assert.h
d)、if/else中i不大于j索引对应的块大小类型的数组,则将j索引的值赋给类型索引数组,例如,j = 0时,i的值可以是 1-16。否则i<j索引对应的块大小类型的数组,则将j索引自增,赋值。上面代码可以简化成:
[cpp]view plaincopy
1. “font-size: 14px;”<if(i < s_blockSizes[j])
2. {
3. ++j;
4. }
5. s_blockSizeLookup[i] = (uint8)j;
虽然效率差了点:-D,但这样更易于理解。
e)、将标志变量置s_blockSizeLookupInitialized为true,表示已经初始化在析构函数~b2BlockAllocator()我们释放了当前块的每个子节点,和整个块。
2)、内存管理函数内存管理函数分为三个:Allocate分配函数;Free释放函数;Clear清理内存函数
关于Allocate函数,代码如下:
[cpp]view plaincopy
1. “font-size: 14px;”<void* b2BlockAllocator::Allocate(int32 size)
2. {
3. if (size == 0)
4. return NULL;
5. //验证size的有效性
6. b2Assert(0 < sizespan>
7. //申请的空间大于规定的最大值,
8. //直接申请,不放到块的链表中去【即m_chunks】
9. if (size < b2_maxBlockSize)
10. {
11. return b2Alloc(size);
12. }
13. //根据要申请的内存大小获取内存类型索引值,并判断有效性
14. int32 index = s_blockSizeLookup[size];
15. b2Assert(0 < indexindexb_blockSizesspan>
16. //查看是否有同类型的未被使用的内存块
17. if (m_freeLists[index])
18. {
19. b2Block* block = m_freeLists[index];
20. m_freeLists[index] = block-<next;
21. return block;
22. }
23. else
24. {
25. //已使用的大小与已申请的块大小相等
26. //重新申请空间
27. if (m_chunkCount == m_chunkSpace)
28. {
29. //获取原来的块头,并保存到oldChunks中
30. b2Chunk* oldChunks = m_chunks;
31. //扩充块空间的大小
32. m_chunkSpace += b2_chunkArrayIncrement;
33. //申请空间,并重新赋值给m_chunks变量
34. m_chunks = (b2Chunk*)b2Alloc(m_chunkSpace * sizeof(b2Chunk));
35. //拷贝内存到m_chunks中
36. memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk));
37. //将最新申请的内存的最后b2_chunkArrayIncrement置,防止程序中读取脏数据
38. //个人感觉如下写法更易于理解,只是效率要慢一点点啦
39. ///memset(m_chunks , 0,m_chunkSpace * sizeof(b2Chunk));
40. ///memcpy(m_chunks, oldChunks, m_chunkCount * sizeof(b2Chunk));
41. memset(m_chunks + m_chunkCount, 0, b2_chunkArrayIncrement * sizeof(b2Chunk));
42. //释放原来的块空间
43. b2Free(oldChunks);
44. }
45. //获取已使用块空间的尾指针
46. b2Chunk* chunk = m_chunks + m_chunkCount;
47. //申请n个块子节点内存
48. //并将地址赋值给块头指针
49. //这样的好处是不需要频繁的去内存中申请空间,不必每个节点都去申请,提高了效率
50. chunk-<blocks = (b2Block*)b2Alloc(b2_chunkSize);
51. //用于调试,正式版本中将关闭_DEBUG宏,故不存在相关代码,以后我们遇到相关代码块也将忽略
52. #if defined(_DEBUG)
53. memset(chunk-<blocks, 0xcd, b2_chunkSize);
54. #endif
55. //获取根据索引块大小,并赋值给块的大小
56. int32 blockSize = s_blockSizes[index];
57. chunk-<blockSize = blockSize;
58. //获取子节点个数
59. //并防止转换时出现错误【个人猜测,有待考证】
60. int32 blockCount = b2_chunkSize / blockSize;
61. b2Assert(blockCount * blockSize < b_chunkSizespan>
62. //将子节点用链表的方式串起来
63. //有人不禁疑惑,这不是刚刚申请的一个连续的内存块吗?
64. //用头指针可以直接访问呀?干嘛要串起来?
65. //好处:
66. //可以自由的操作每个子节点,例如、释放、访问、其它方式链接等,在后面我们将看见
67. for (int32 i = 0; i < blockCount-ispan>
68. {
69. b2Block* block = (b2Block*)((int8*)chunk-<blocks + blockSize * i);
70. b2Block* next = (b2Block*)((int8*)chunk-<blocks + blockSize * (i + 1));
71. block-<next = next;
72. }
73. //获取最后一个内存块的子节点
74. //并将子节点的下一个节点置空
75. b2Block* last = (b2Block*)((int8*)chunk-<blocks + blockSize * (blockCount – 1));
76. last-<next = NULL;
77. //将申请且未使用的指针保存到m_freeLists对应类型的数组中
78. m_freeLists[index] = chunk-<blocks-<next;
79. //当前已使用的块空间节点总数
80. ++m_chunkCount;
81. //返回块的头指针
82. return chunk-<blocks;
83. }
代码的解释如上述,在此就不啰嗦了,不过我们总体分析一下,它的逻辑是:
a)、将内存按大小分为16,32,64,96,128,160,192,224,256…640等b2_blockSizes【即14】类,并按照顺序保存到数组s_blockSizes中。
b)、通过申请size的大小,判断是否大于b2_maxBlockSize,如果大于则直接分配。
c)、否则通过size,传递给s_blockSizeLookup数组找到所需要申请的类型index,将index的值在传递给链表数组m_freelists[index],查找是否有子节点,有则直接返回子节点。
d)、否则将判断m_chunks指向的动态数组是否已用完,若用完则扩充块空间大小加b2_chunkArrayIncrement,重新申请空间,并将空间的内存拷贝到现在的空间中,并释放原内存空间。
e)、通过m_chunks+m_chunkCount获取块空间的m_chunks动态数组的未被使用的空间元素,申请大小为b2_chunkSize的内存,并将其分成对应类型的n块空间,并将这n个子节点串链起来,形成链表。将链表的下一个指针保存到对应类型的m_freeLists数组中,同时返回头指针作为申请的内存地址。
关于Free函数,代码和注释如下:
[cpp]view plaincopy
1. “font-size: 14px;”<
2. void b2BlockAllocator::Free(void* p, int32 size)
3. {
4. //判断检测size是否有效
5. //并作相应的处理
6. if (size == 0)
7. {
8. return;
9. }
10.
11. b2Assert(0 < sizespan>
12.
13. if (size < b2_maxBlockSize)
14. {
15. b2Free(p);
16. return;
17. }
18. //根据内存大小获取索引值,并判断是否有效
19. int32 index = s_blockSizeLookup[size];
20. b2Assert(0 < indexindexb_blockSizesspan>
21.
22. #ifdef _DEBUG
23. // Verify the memory address and size is valid.
24. int32 blockSize = s_blockSizes[index];
25. bool found = false;
26. for (int32 i = 0; i < m_chunkCountispan>
27. {
28. b2Chunk* chunk = m_chunks + i;
29. if (chunk-<blockSize != blockSize)
30. {
31. b2Assert( (int8*)p + blockSize < intchunk->blocks ||
32. (int8*)chunk-<blocks + b2_chunkSize < intpspan>
33. }
34. else
35. {
36. if ((int8*)chunk-<blocks < intpintpblockSize=” (int8*)chunk-” >blocks + b2_chunkSize)
37. {
38. found = true;
39. }
40. }
41. }
42.
43. b2Assert(found);
44.
45. memset(p, 0xfd, blockSize);
46. #endif
47. //获取块的块的头指针并插入到相应的空闲链表的头部【注意是子节点从链表头部插入】,并保存相应的头指针到m_freeLists中去
48. b2Block* block = (b2Block*)p;
49. block-<next = m_freeLists[index];
50. m_freeLists[index] = block;
51. }
同样,_DEBUG宏类的我们不做讨论。这里说明一下,若内存小于等于b2_maxBlockSize时,此时内存空间并没有释放,而是将链接到相应类型的空闲链表中,并且是从链表头部插入此节点的,对此这也是很容易做到的,这也是刚刚申请空间将连续的空间分割,再次链接成链表的原因。
再看Clear函数
[cpp]view plaincopy
1. “font-size: 14px;”<void b2BlockAllocator::Clear()
2. {
3. //释放当前已使用的块空间大小
4. for (int32 i = 0; i < m_chunkCountispan>
5. {
6. b2Free(m_chunks[i].blocks);
7. }
8.
9. m_chunkCount = 0;
10. //清空块
11. memset(m_chunks, 0, m_chunkSpace * sizeof(b2Chunk));
12. //清空未被使用的内存块链表类型数组
13. memset(m_freeLists, 0, sizeof(m_freeLists));
14. }
只是释放了形成链表的块内存,m_chunks和m_freeLists也只是清空其内容,真正释放它们是在上面说的类的析构函数中。
ok,不多说了,有什么错误、不妥之处,希望大家能多多指正。也希望和大家多多交流。
Box2d上有两个和栈有关的类,它们分别是b2StackAllocator和b2GrowableStack。
B2StackAllocator主要是为了运行一个步长时满足box2d需要的临时内存空间,作为栈分配器来防止单步堆分配。
B2GrowableStack主要是为了满足动态树b2DynaicTree中光线投射和区域查询所需要的临时内存空间,这个到动态树的时候再做详细研究。
下面我们就来看这两个类是如何实现的。
1、 b2StackAllocator类
首先我们看它头文件b2StackAllocator.h
看下相关常量和结构体的定义:
[cpp]view plaincopy
1. //栈中内存池的大小
2. const int32 b2_stackSize = 100 * 1024; // 100k
3. //栈元素的最大数量
4. const int32 b2_maxStackEntries = 32;
5. //栈实体定义
6. struct b2StackEntry
7. {
8. char* data; //头指针
9. int32 size; //大小
10. bool usedMalloc; //是否使用
11. };
代码上面有注释,不多说了。
下面看看b2StackAllocator类的定义吧(好期待(⊙o⊙)哦)
[cpp]view plaincopy
1. // 这是一个栈分配器用于每一步都快速的分配
2. // 你必须成对使用allocate/free这对函数。
3. // 如果你使用allocate/free次数不同时,将会出现断言。
4. class b2StackAllocator
5. {
6. public:
7.
8. /**************************************************************************
9. * 功能描述:构造函数
10. * 参数说明:(void)
11. * 返 回 值:无
12. **************************************************************************/
13. b2StackAllocator();
14. /**************************************************************************
15. * 功能描述:析构函数
16. * 参数说明:(void)
17. * 返 回 值:无
18. **************************************************************************/
19. ~b2StackAllocator();
20. /**************************************************************************
21. * 功能描述:申请内存函数
22. * 参数说明:size :需要申请的内存大小
23. * 返 回 值:申请的内存头指针
24. **************************************************************************/
25. void* Allocate(int32 size);
26. /**************************************************************************
27. * 功能描述:释放内存函数
28. * 参数说明:p :释放内存的头指针
29. * 返 回 值:(void)
30. **************************************************************************/
31. void Free(void* p);
32. /**************************************************************************
33. * 功能描述:获取已申请的内存容量的最大值
34. * 参数说明:(void)
35. * 返 回 值:曾经进栈过所有元素【不管现在是否出栈】使用的内存容量
36. **************************************************************************/
37. int32 GetMaxAllocation() const;
38.
39. private:
40. //栈的内存池,用于栈子节点的内存开辟
41. char m_data[b2_stackSize];
42. //在栈的内存池中,已使用的内存大小
43. int32 m_index;
44. //栈中的所有元素使用内存大小
45. int32 m_allocation;
46. //曾经进栈过所有元素【不管现在是否出栈】使用的内存容量
47. //注意该变量在对象销毁之前只增不减
48. int32 m_maxAllocation;
49. //栈实体的数组
50. b2StackEntry m_entries[b2_maxStackEntries];
51. //栈中元素的数量
52. int32 m_entryCount;
53. };
同样不多说了,看注释。我们再来看看b2StakAllocator是如何实现的:
[cpp]view plaincopy
1. b2StackAllocator::b2StackAllocator()
2. {
3. //初始化相关变量
4. m_index = 0;
5. m_allocation = 0;
6. m_maxAllocation = 0;
7. m_entryCount = 0;
8. }
9. b2StackAllocator::~b2StackAllocator()
10. {
11. //验证内存是否已完全释放
12. b2Assert(m_index == 0);
13. //验证元素是否已完全出栈
14. b2Assert(m_entryCount == 0);
15. }
下面是Allocate、Free、GetMaxAllocation函数,看代码:
[cpp]view plaincopy
1. void* b2StackAllocator::Allocate(int32 size)
2. {
3. //验证栈中元素的有效性,防止内存溢出
4. b2Assert(m_entryCount < b_maxStackEntriesspan>
5. //获取栈实体头指针
6. b2StackEntry* entry = m_entries + m_entryCount;
7. //实体大小
8. entry-<size = size;
9. //当内存池m_data已使用的大小加需要申请的大小大于内存池的总容量时,则在堆上申请
10. if (m_index + size < b2_stackSize)
11. {
12. //申请大小为size的内存,并标记是在堆上申请的
13. entry-<data = (char*)b2Alloc(size);
14. entry-<usedMalloc = true;
15. }
16. else
17. {
18. //从m_data中获取内存,并标记不是在堆上申请的
19. //同时修改m_index的值
20. entry-<data = m_data + m_index;
21. entry-<usedMalloc = false;
22. m_index += size;
23. }
24. //增加栈中的所有元素使用内存大小
25. m_allocation += size;
26. //修改内存容量的最大值
27. m_maxAllocation = b2Max(m_maxAllocation, m_allocation);
28. //增加栈中元素的数量
29. ++m_entryCount;
30. //返回栈中元素的内存头指针
31. return entry-<data;
32. }
33.
34. void b2StackAllocator::Free(void* p)
35. {
36. //验证栈中元素的有效性
37. b2Assert(m_entryCount < 0);
38. //栈中数量减1,在这里用数组模拟了出栈
39. b2StackEntry* entry = m_entries + m_entryCount – 1;
40. b2Assert(p == entry-<data);
41. //是否是在堆上申请的
42. if (entry-<usedMalloc)
43. {
44. //释放p
45. b2Free(p);
46. }
47. else
48. {
49. //将索引值减去栈实体的内存大小
50. m_index -= entry-<size;
51. }
52. //减去已释放的内存大小
53. m_allocation -= entry-<size;
54. //元素数量减少
55. –m_entryCount;
56. //将指针置空,防止野指针
57. p = NULL;
58. }
59. int32 b2StackAllocator::GetMaxAllocation() const
60. {
61. return m_maxAllocation;
62. }
对于Allocate函数,我们用原先分配了大小b2_stackSize的内存池辅助数组。同时也用m_index记录了已使用的内存池的大小。若内存池中剩余内存不够则在堆上申请,否则在内存池中获取相应大小的内存。
同样Free函数也有两种情况,在堆上申请的内存直接释放,在内存池中申请的内存返还到内存池中,不用释放。
GetMaxAllocation函数在同一个对象的生命周期里是返回的值是只增不减的。
2、 b2GrowableStack类
本类定义、实现均在b2GrowableStack.h文件中
[cpp]view plaincopy
1. //这是一个可增长的先进后出初始容量为N的栈
2. //如果栈的大小达到初始容量,则在堆中增加栈的大小
3. template < span>typename T, int32 N<
4. class b2GrowableStack
5. {
6. public:
7. /**************************************************************************
8. * 功能描述:栈构造函数,初始化相关数据
9. * 参数说明:(void)
10. * 返 回 值:(void)
11. **************************************************************************/
12. b2GrowableStack()
13. {
14. m_stack = m_array;
15. m_count = 0;
16. m_capacity = N;
17. }
18. /**************************************************************************
19. * 功能描述:栈析构函数,释放相关内存
20. * 参数说明:(void)
21. * 返 回 值:(void)
22. **************************************************************************/
23. ~b2GrowableStack()
24. {
25. if (m_stack != m_array)
26. {
27. b2Free(m_stack);
28. m_stack = NULL;
29. }
30. }
31.
32. /**************************************************************************
33. * 功能描述:进栈操作
34. * 参数说明:element :进栈元素
35. * 返 回 值:(void)
36. **************************************************************************/
37. void Push(const T& element)
38. {
39. //栈已满
40. if (m_count == m_capacity)
41. {
42. //获取栈头指针并保存到old中
43. T* old = m_stack;
44. //将栈的容量扩充到原来的2倍
45. m_capacity *= 2;
46. //申请内存,并保存到m_stack中
47. m_stack = (T*)b2Alloc(m_capacity * sizeof(T));
48. //将原来的内容整体拷贝到m_stack中去
49. std::memcpy(m_stack, old, m_count * sizeof(T));
50. if (old != m_array)
51. {
52. //释放old指向的内存
53. b2Free(old);
54. }
55. }
56. //进栈,并将元素个数自加
57. m_stack[m_count] = element;
58. ++m_count;
59. }
60.
61. /**************************************************************************
62. * 功能描述:出栈操作
63. * 参数说明:(void)
64. * 返 回 值:返回最近进入栈的值
65. **************************************************************************/
66. T Pop()
67. {
68. //验证元素个数的有效性
69. b2Assert(m_count < 0);
70. //元素个数自减,并出栈
71. –m_count;
72. return m_stack[m_count];
73. }
74. /**************************************************************************
75. * 功能描述:获取栈中元素的个数
76. * 参数说明:(void)
77. * 返 回 值:栈中元素的个数
78. **************************************************************************/
79. int32 GetCount()
80. {
81. return m_count;
82. }
83.
84. private:
85. //栈头指针
86. T* m_stack;
87. //辅助数组
88. T m_array[N];
89. //元素的个量
90. int32 m_count;
91. //栈的容量
92. int32 m_capacity;
93. };
该类是个模板类,所有元素的类型和栈的大小均有使用的时候自己定义,同样是由内存池即辅助数组模拟的入栈和出栈。不同地方是当内存池不足时,b2GrowableStack会重新申请一个是原来一倍容量更大的新的内存池,并拷贝旧内存池中的内容到新的内存池中,同时释放除辅助数组以外的旧的内存池,不过多次扩充内存对将会有一定的影响,最好能够使用之前先估算一下所需内存池的大小。对于push/pop函数,均用数组模拟的进出栈。其他也没啥好说的了。
为了满足Box2d中所要用到的数学知识,在Box2d的公共模块中,包含了一个小巧而简便的向量矩阵的数学库。这部分所有的定义实现均有结构体实现的,所以其内部的成员均对外部公开,所以你可以任意使用和访问。该部分主要由以下内容:
a)、向量,包括二维列向量和三维列向量
b)、矩阵,包括2X2矩阵和3X3矩阵
c)、旋度、扫描、和变换的实现
d)、其他部分的实现
一、向量部分
1、 二维列向量的实现
[cpp]view plaincopy
1. //2d列向量
2. struct b2Vec2
3. {
4. /**************************************************************************
5. * 功能描述:构造函数,不做任何事,仅仅为了性能
6. * 参数说明: (void)
7. * 返 回 值: (void)
8. **************************************************************************/
9. b2Vec2() {}
10. /**************************************************************************
11. * 功能描述:构造函数,构建使用的坐标
12. * 参数说明: x:x坐标
13. y:y坐标
14. * 返 回 值: (void)
15. **************************************************************************/
16. b2Vec2(float32 x, float32 y) : x(x), y(y) {}
17. /**************************************************************************
18. * 功能描述:将向量置0
19. * 参数说明: (void)
20. * 返 回 值: (void)
21. **************************************************************************/
22. void SetZero() { x = 0.0f; y = 0.0f; }
23.
24. /**************************************************************************
25. * 功能描述:将向量设置成指定的坐标
26. * 参数说明: x_:x坐标
27. y_:y坐标
28. * 返 回 值: (void)
29. **************************************************************************/
30. void Set(float32 x_, float32 y_) { x = x_; y = y_; }
31.
32. /**************************************************************************
33. * 功能描述:将向量取反,重载 – 运算符
34. * 参数说明: (void)
35. * 返 回 值: 一个新的二维向量
36. **************************************************************************/
37. b2Vec2 operator -() const { b2Vec2 v; v.Set(-x, -y); return v; }
38.
39. /**************************************************************************
40. * 功能描述:读取指定索引的元素,重载 () 运算符
41. * 参数说明: i :索引值
42. * 返 回 值: 列中对应索引元素值
43. **************************************************************************/
44. float32 operator () (int32 i) const
45. {
46. return (&x)[i];
47. }
48.
49. /**************************************************************************
50. * 功能描述:改写指定索引的元素,重载 () 运算符
51. * 参数说明: i :索引值
52. * 返 回 值: 列中对应索引元素的引用
53. **************************************************************************/
54. float32& operator () (int32 i)
55. {
56. return (&x)[i];
57. }
58.
59. /**************************************************************************
60. * 功能描述:与另一个向量相加
61. * 参数说明: v :向量的引用
62. * 返 回 值: (void)
63. **************************************************************************/
64. void operator += (const b2Vec2& v)
65. {
66. x += v.x; y += v.y;
67. }
68. /**************************************************************************
69. * 功能描述:与另一个向量相减
70. * 参数说明: v :向量的引用
71. * 返 回 值: (void)
72. **************************************************************************/
73. void operator -= (const b2Vec2& v)
74. {
75. x -= v.x; y -= v.y;
76. }
77. /**************************************************************************
78. * 功能描述:与标量相乘
79. * 参数说明: a :标量
80. * 返 回 值: (void)
81. **************************************************************************/
82. void operator *= (float32 a)
83. {
84. x *= a; y *= a;
85. }
86. /**************************************************************************
87. * 功能描述:获取向量的长度
88. * 参数说明: (void)
89. * 返 回 值: 向量长度
90. **************************************************************************/
91. float32 Length() const
92. {
93. return b2Sqrt(x * x + y * y);
94. }
95. /**************************************************************************
96. * 功能描述:获取向量的长度的平方。出于性能要求,
97. 使用此函数代替b2Vec2::Legth函数(如果可能的话)
98. * 参数说明: (void)
99. * 返 回 值: 向量长度的平方
100. **************************************************************************/
101. float32 LengthSquared() const
102. {
103.return x * x + y * y;
104. }
105.
106./**************************************************************************
107. * 功能描述:将该向量转变成单位向量
108. * 参数说明: (void)
109. * 返 回 值: 向量长度[注意是未转变之前的长度]
110. **************************************************************************/
111. float32 Normalize()
112. {
113.//获取该向量的长度并验证其有效性
114. float32 length = Length();
115.if (length < b_epsilonspan>
116. {
117.return 0.0f;
118. }
119.//获取要转换单位向量的因子,并与x、y轴相乘
120. float32 invLength = 1.0f / length;
121. x *= invLength;
122. y *= invLength;
123.
124.return length;
125. }
126./**************************************************************************
127. * 功能描述:该向量包含的坐标值是否有效
128. * 参数说明: (void)
129. * 返 回 值: 是否有效
130. **************************************************************************/
131.bool IsValid() const
132. {
133.return b2IsValid(x) && b2IsValid(y);
134. }
135./**************************************************************************
136. * 功能描述:获取斜对称向量(反对称向量),dot(skew_vec, other) == cross(vec, other)
137. * 参数说明: (void)
138. * 返 回 值: 斜对称向量(反对称向量)
139. **************************************************************************/
140. b2Vec2 Skew() const
141. {
142.return b2Vec2(-y, x);
143. }
144.//向量元素的x,y值
145. float32 x, y;
146.};
2、三维列向量的实现
[cpp]view plaincopy
1. //2d三维列向量
2. struct b2Vec3
3. {
4. /**************************************************************************
5. * 功能描述:默认构造函数,不做任何事,仅仅为了性能
6. * 参数说明: (void)
7. * 返 回 值: (void)
8. **************************************************************************/
9. b2Vec3() {}
10.
11. /**************************************************************************
12. * 功能描述:构造函数,构建使用的坐标
13. * 参数说明: x:x坐标
14. y:y坐标
15. * 返 回 值: (void)
16. **************************************************************************/
17. b2Vec3(float32 x, float32 y, float32 z) : x(x), y(y), z(z) {}
18.
19. /**************************************************************************
20. * 功能描述:将向量置0
21. * 参数说明: (void)
22. * 返 回 值: (void)
23. **************************************************************************/
24. void SetZero() { x = 0.0f; y = 0.0f; z = 0.0f; }
25. /**************************************************************************
26. * 功能描述:将向量设置成指定的坐标
27. * 参数说明: x_:x坐标
28. y_:y坐标
29. x_:z坐标
30. * 返 回 值: (void)
31. **************************************************************************/
32. void Set(float32 x_, float32 y_, float32 z_) { x = x_; y = y_; z = z_; }
33.
34. /**************************************************************************
35. * 功能描述:将向量取反,重载 – 运算符
36. * 参数说明: (void)
37. * 返 回 值: 一个新的三维向量
38. **************************************************************************/
39. b2Vec3 operator -() const { b2Vec3 v; v.Set(-x, -y, -z); return v; }
40.
41. /**************************************************************************
42. * 功能描述:与另一个向量相加
43. * 参数说明: v :向量的引用
44. * 返 回 值: (void)
45. **************************************************************************/
46. void operator += (const b2Vec3& v)
47. {
48. x += v.x; y += v.y; z += v.z;
49. }
50. /**************************************************************************
51. * 功能描述:与另一个向量相减
52. * 参数说明: v :向量的引用
53. * 返 回 值: (void)
54. **************************************************************************/
55. void operator -= (const b2Vec3& v)
56. {
57. x -= v.x; y -= v.y; z -= v.z;
58. }
59.
60. /**************************************************************************
61. * 功能描述:与一个标量相乘
62. * 参数说明: s :标量
63. * 返 回 值: (void)
64. **************************************************************************/
65. void operator *= (float32 s)
66. {
67. x *= s; y *= s; z *= s;
68. }
69. //向量的元素值
70. float32 x, y, z;
71. };
二、矩阵
1、2X2矩阵的实现
[cpp]view plaincopy
1. //2X2矩阵,存储方式以列为主的顺序
2. struct b2Mat22
3. {
4. /**************************************************************************
5. * 功能描述:默认构造函数不做任何事情(仅仅为了性能)
6. * 参数说明: (void)
7. * 返 回 值: (void)
8. **************************************************************************/
9. b2Mat22() {}
10. /**************************************************************************
11. * 功能描述:使用列向量构造这个矩阵
12. * 参数说明: c1 :第一列向量
13. c2 :第二列向量
14. * 返 回 值: (void)
15. **************************************************************************/
16. b2Mat22(const b2Vec2& c1, const b2Vec2& c2)
17. {
18. ex = c1;
19. ey = c2;
20. }
21.
22. /**************************************************************************
23. * 功能描述:使用标量构造这个矩阵
24. * 参数说明: a11-a22:四个元素值
25. * 返 回 值: (void)
26. **************************************************************************/
27. b2Mat22(float32 a11, float32 a12, float32 a21, float32 a22)
28. {
29. ex.x = a11; ex.y = a21;
30. ey.x = a12; ey.y = a22;
31. }
32.
33. /**************************************************************************
34. * 功能描述:使用列实例话这个矩阵
35. * 参数说明: c1:列向量
36. c2:列向量
37. * 返 回 值: (void)
38. **************************************************************************/
39. void Set(const b2Vec2& c1, const b2Vec2& c2)
40. {
41. ex = c1;
42. ey = c2;
43. }
44. /**************************************************************************
45. * 功能描述:设置成单位矩阵
46. * 参数说明: (void)
47. * 返 回 值: (void)
48. **************************************************************************/
49. void SetIdentity()
50. {
51. ex.x = 1.0f; ey.x = 0.0f;
52. ex.y = 0.0f; ey.y = 1.0f;
53. }
54.
55. /**************************************************************************
56. * 功能描述:将该矩阵置0
57. * 参数说明: (void)
58. * 返 回 值: (void)
59. **************************************************************************/
60. void SetZero()
61. {
62. ex.x = 0.0f; ey.x = 0.0f;
63. ex.y = 0.0f; ey.y = 0.0f;
64. }
65. /**************************************************************************
66. * 功能描述:获取逆矩阵,
67. 逆矩阵B = A的伴随矩阵/A的行列式
68. 参照 http://zh.wikipedia.org/wiki/逆矩阵
69. * 参数说明: (void)
70. * 返 回 值: 逆矩阵
71. **************************************************************************/
72. b2Mat22 GetInverse() const
73. {
74. float32 a = ex.x, b = ey.x, c = ex.y, d = ey.y;
75. b2Mat22 B;
76. float32 det = a * d – b * c; //获取行列式的值
77. if (det != 0.0f)
78. {
79. det = 1.0f / det;
80. }
81. B.ex.x = det * d; B.ey.x = -det * b;
82. B.ex.y = -det * c; B.ey.y = det * a;
83. return B;
84. }
85. /**************************************************************************
86. * 功能描述:解决A*x = b,其中b是一个列向量
87. 这比一次性求反计算更有效率
88. * 参数说明: b : 列向量
89. * 返 回 值: 列向量
90. **************************************************************************/
91. b2Vec2 Solve(const b2Vec2& b) const
92. {
93. float32 a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y;
94. //获取行列式的值
95. float32 det = a11 * a22 – a12 * a21;
96. if (det != 0.0f)
97. {
98. det = 1.0f / det;
99. }
100. b2Vec2 x;
101.//[ a22 -a12] * [b.x] = [a22 * b.x – a12*b.y]
102.//[-a21 a11] [b.y] [a11 * b.y – a21*b.x]
103. x.x = det * (a22 * b.x – a12 * b.y);
104. x.y = det * (a11 * b.y – a21 * b.x);
105.return x;
106. }
107.//两个列向量
108. b2Vec2 ex, ey;
109.};
2、3X3矩阵的实现
[cpp]view plaincopy
1. //3X3矩阵,存储方式以列为主的顺序
2. struct b2Mat33
3. {
4. /**************************************************************************
5. * 功能描述:默认构造函数不做任何事情(仅仅为了性能)
6. * 参数说明: (void)
7. * 返 回 值: (void)
8. **************************************************************************/
9. b2Mat33() {}
10.
11. /**************************************************************************
12. * 功能描述:使用列向量构造这个矩阵
13. * 参数说明: c1 :第一列向量
14. c2 :第二列向量
15. * 返 回 值: (void)
16. **************************************************************************/
17. b2Mat33(const b2Vec3& c1, const b2Vec3& c2, const b2Vec3& c3)
18. {
19. ex = c1;
20. ey = c2;
21. ez = c3;
22. }
23.
24. /**************************************************************************
25. * 功能描述:将该矩阵置0
26. * 参数说明: (void)
27. * 返 回 值: (void)
28. **************************************************************************/
29. void SetZero()
30. {
31. ex.SetZero();
32. ey.SetZero();
33. ez.SetZero();
34. }
35.
36. /**************************************************************************
37. * 功能描述:解决A*x = b,其中b是一个三维列向量
38. 这比一次性求反计算更有效率
39. * 参数说明: b : 三维列向量
40. * 返 回 值: 三维列向量
41. **************************************************************************/
42. b2Vec3 Solve33(const b2Vec3& b) const;
43. /**************************************************************************
44. * 功能描述:解决A*x = b,其中b是一个二维列向量
45. 这比一次性求反计算更有效率
46. 仅仅解决2X2的矩阵等式
47. * 参数说明: b : 三维列向量
48. * 返 回 值: 三维列向量
49. **************************************************************************/
50. b2Vec2 Solve22(const b2Vec2& b) const;
51. /**************************************************************************
52. * 功能描述:获取2X2的逆矩阵,
53. 如果出现异常则置为0矩阵
54. 逆矩阵B = A的伴随矩阵/A的行列式
55. * 参数说明: (void)
56. * 返 回 值: (void)
57. **************************************************************************/
58. void GetInverse22(b2Mat33* M) const;
59. /**************************************************************************
60. * 功能描述:获取3X3的对称逆矩阵
61. 如果出现异常则置为0矩阵
62. * 参数说明: (void)
63. * 返 回 值: (void)
64. **************************************************************************/
65. void GetSymInverse33(b2Mat33* M) const;
66. //矩阵的3个列向量
67. b2Vec3 ex, ey, ez;
68. };
在ccp文件中Mat33结构部分函数的实现:
[cpp]view plaincopy
1. //解决 A * x =b,其中b是列向量
2. b2Vec3 b2Mat33::Solve33(const b2Vec3& b) const
3. {
4. float32 det = b2Dot(ex, b2Cross(ey, ez));
5. if (det != 0.0f)
6. {
7. det = 1.0f / det;
8. }
9. b2Vec3 x;
10. x.x = det * b2Dot(b, b2Cross(ey, ez));
11. x.y = det * b2Dot(ex, b2Cross(b, ez));
12. x.z = det * b2Dot(ex, b2Cross(ey, b));
13. return x;
14. }
15. //解决 A * x =b,其中b是列向量
16. b2Vec2 b2Mat33::Solve22(const b2Vec2& b) const
17. {
18. float32 a11 = ex.x, a12 = ey.x, a21 = ex.y, a22 = ey.y;
19. float32 det = a11 * a22 – a12 * a21;
20. if (det != 0.0f)
21. {
22. det = 1.0f / det;
23. }
24. b2Vec2 x;
25. x.x = det * (a22 * b.x – a12 * b.y);
26. x.y = det * (a11 * b.y – a21 * b.x);
27. return x;
28. }
29.
30. //获取2X2的逆矩阵
31. //如果出现异常则置为0矩阵
32. void b2Mat33::GetInverse22(b2Mat33* M) const
33. {
34. float32 a = ex.x, b = ey.x, c = ex.y, d = ey.y;
35. float32 det = a * d – b * c;
36. if (det != 0.0f)
37. {
38. det = 1.0f / det;
39. }
40.
41. M-<ex.x = det * d; M-<ey.x = -det * b; M-<ex.z = 0.0f;
42. M-<ex.y = -det * c; M-<ey.y = det * a; M-<ey.z = 0.0f;
43. M-<ez.x = 0.0f; M-<ez.y = 0.0f; M-<ez.z = 0.0f;
44. }
45. //获取3X3的对称矩阵的逆矩阵,如果出现异常则置为0矩阵
46. void b2Mat33::GetSymInverse33(b2Mat33* M) const
47. {
48. float32 det = b2Dot(ex, b2Cross(ey, ez));
49. if (det != 0.0f)
50. {
51. det = 1.0f / det;
52. }
53.
54. float32 a11 = ex.x, a12 = ey.x, a13 = ez.x;
55. float32 a22 = ey.y, a23 = ez.y;
56. float32 a33 = ez.z;
57.
58. M-<ex.x = det * (a22 * a33 – a23 * a23);
59. M-<ex.y = det * (a13 * a23 – a12 * a33);
60. M-<ex.z = det * (a12 * a23 – a13 * a22);
61.
62. M-<ey.x = M-<ex.y;
63. M-<ey.y = det * (a11 * a33 – a13 * a13);
64. M-<ey.z = det * (a13 * a12 – a11 * a23);
65.
66. M-<ez.x = M-<ex.z;
67. M-<ez.y = M-<ey.z;
68. M-<ez.z = det * (a11 * a22 – a12 * a12);
69. }
三、旋度、转换、扫描部分
1、旋度的实现
[cpp]view plaincopy
1. //旋度 http://zh.wikipedia.org/wiki/旋度
2. struct b2Rot
3. {
4. /**************************************************************************
5. * 功能描述:默认构造函数不做任何事情(仅仅为了性能)
6. * 参数说明: (void)
7. * 返 回 值: (void)
8. **************************************************************************/
9. b2Rot() {}
10. /**************************************************************************
11. * 功能描述:用角的弧度初始化
12. * 参数说明: anlge:弧度值
13. * 返 回 值: (void)
14. **************************************************************************/
15. explicit b2Rot(float32 angle)
16. {
17. /// TODO_ERIN optimize
18. s = sinf(angle);
19. c = cosf(angle);
20. }
21. /**************************************************************************
22. * 功能描述:用角的弧度设置
23. * 参数说明: anlge:弧度值
24. * 返 回 值: (void)
25. **************************************************************************/
26. void Set(float32 angle)
27. {
28. /// TODO_ERIN optimize
29. s = sinf(angle);
30. c = cosf(angle);
31. }
32. /**************************************************************************
33. * 功能描述:设置成单位旋度
34. * 参数说明: (void)
35. * 返 回 值: (void)
36. **************************************************************************/
37. void SetIdentity()
38. {
39. s = 0.0f;
40. c = 1.0f;
41. }
42. /**************************************************************************
43. * 功能描述:用弧度得到角度
44. * 参数说明: (void)
45. * 返 回 值: 角度值
46. **************************************************************************/
47. float32 GetAngle() const
48. {
49. return b2Atan2(s, c);
50. }
51. /**************************************************************************
52. * 功能描述:获取x-axis
53. * 参数说明: (void)
54. * 返 回 值: 列向量
55. **************************************************************************/
56. b2Vec2 GetXAxis() const
57. {
58. return b2Vec2(c, s);
59. }
60. /**************************************************************************
61. * 功能描述:获取u-axis
62. * 参数说明: (void)
63. * 返 回 值: 列向量
64. **************************************************************************/
65. b2Vec2 GetYAxis() const
66. {
67. return b2Vec2(-s, c);
68. }
69.
70. //定义sin和cos
71. float32 s, c;
72. };
2、变换的实现
[cpp]view plaincopy
1. ///变换包括平移和旋度,它是用来表示位置和精确方向的框架
2. struct b2Transform
3. {
4. /**************************************************************************
5. * 功能描述:默认构造函数,什么都不做
6. * 参数说明: (void)
7. * 返 回 值: (void)
8. **************************************************************************/
9. b2Transform() {}
10. /**************************************************************************
11. * 功能描述:用一个位置向量和一个旋度进行初始化
12. * 参数说明: position :位置向量
13. rotation :旋度
14. * 返 回 值: (void)
15. **************************************************************************/
16. b2Transform(const b2Vec2& position, const b2Rot& rotation) : p(position), q(rotation) {}
17.
18. /**************************************************************************
19. * 功能描述:设置单位变换
20. * 参数说明: (void)
21. * 返 回 值: (void)
22. **************************************************************************/
23. void SetIdentity()
24. {
25. p.SetZero();
26. q.SetIdentity();
27. }
28. /**************************************************************************
29. * 功能描述:基于角的弧度和位置设置
30. * 参数说明: position:位置向量
31. angle :角的弧度
32. * 返 回 值: (void)
33. **************************************************************************/
34. void Set(const b2Vec2& position, float32 angle)
35. {
36. p = position;
37. q.Set(angle);
38. }
39. //成员变量,一个向量和一个旋度
40. b2Vec2 p;
41. b2Rot q;
42. };
3、扫描的实现
[cpp]view plaincopy
1. //描述运动的body/shape的TOI(Time of Impact)的计算。
2. //形状相对于主体原点定义的,这可能没有一致的原点。
3. //但是,为了支持动态我们必须篡改质心位置
4. struct b2Sweep
5. {
6. /**************************************************************************
7. * 功能描述:在特定时间里获得窜改变换
8. * 参数说明: xfb :变换的指针
9. beta :一个因子,在[0,1]之间,0表示alpha0
10. * 返 回 值: (void)
11. **************************************************************************/
12. void GetTransform(b2Transform* xfb, float32 beta) const;
13. /**************************************************************************
14. * 功能描述:推进缓慢向前移动,产生一个新的初始状态
15. * 参数说明:alpha:新的初始时间
16. * 返 回 值: (void)
17. **************************************************************************/
18. void Advance(float32 alpha);
19. /**************************************************************************
20. * 功能描述:标准化角度
21. * 参数说明: (void)
22. * 返 回 值: (void)
23. **************************************************************************/
24. void Normalize();
25.
26. b2Vec2 localCenter; ///质心位置
27. b2Vec2 c0, c; ///世界的中心位置
28. float32 a0, a; ///世界角度
29. //分数,当前时间步长,范围[0,1]之间
30. float32 alpha0;
31. };
四、其它
在b2Math.h的开始部分
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:用来确保一个浮点数不是一个NaN或无穷大
3. * 参数说明: x :数值
4. * 返 回 值: true :有效
5. false: 无效
6. **************************************************************************/
7. inlinebool b2IsValid(float32 x)
8. {
9. if (x != x)
10. {
11. // NaN.
12. returnfalse;
13. }
14.
15. float32 infinity = std::numeric_limits::infinity();
16. return -infinity < xxinfinityspan>
17. }
18.
19. /**************************************************************************
20. * 功能描述:一个近似且快速的平方根倒数算法[x^ (-1/2) ]
21. 可以参照 http://zh.wikipedia.org/wiki/平方根倒数速算法
22. 或 http://en.wikipedia.org/wiki/Fast_inverse_square_root
23. * 参数说明: x :数值
24. * 返 回 值: 平方根倒数
25. **************************************************************************/
26. inline float32 b2InvSqrt(float32 x)
27. {
28. union
29. {
30. float32 x;
31. int32 i;
32. } convert;
33.
34. convert.x = x;
35. float32 xhalf = 0.5f * x;
36. convert.i = 0x5f3759df – (convert.i << 1);
37. x = convert.x;
38. x = x * (1.5f – xhalf * x * x); //第一次牛顿迭代
39. return x;
40. }
41. //平方根宏
42. #define b2Sqrt(x) std::sqrt(x)
43. //对于任意不同时等于0的实参数x和y,
44. //atan2(y,x)所表达的意思是坐标原点为起点,
45. //指向(y,x)的射线在坐标平面上与x轴正方向之间的角的角度度
46. //参照 http://zh.wikipedia.org/wiki/Atan2
47. #define b2Atan2(y, x) std::atan2(y, x)
48. /**************************************************************************
49. * 功能描述:计算两个向量的点积,注:a = b时,表示向量长度的平方
50. * 参数说明: a :二维列向量
51. b :二维列向量
52. * 返 回 值: 点积值
53. **************************************************************************/
54. inline float32 b2Dot(const b2Vec2& a, const b2Vec2& b)
55. {
56. return a.x * b.x + a.y * b.y;
57. }
58.
59. /**************************************************************************
60. * 功能描述:计算两个向量的叉积,在2d中这是一个标量
61. * 参数说明: a :二维列向量
62. b :二维列向量
63. * 返 回 值: 叉积值
64. **************************************************************************/
65. inline float32 b2Cross(const b2Vec2& a, const b2Vec2& b)
66. {
67. return a.x * b.y – a.y * b.x;
68. }
结尾部分:
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:计算向量和标量的叉积
3. * 参数说明: a :二维列向量
4. s :标量
5. * 返 回 值: 二维列向量
6. **************************************************************************/
7. inline b2Vec2 b2Cross(const b2Vec2& a, float32 s)
8. {
9. return b2Vec2(s * a.y, -s * a.x);
10. }
11.
12. /**************************************************************************
13. * 功能描述:计算标量和向量的叉积
14. * 参数说明: s :标量
15. a :二维列向量
16. * 返 回 值: 二维列向量
17. **************************************************************************/
18. inline b2Vec2 b2Cross(float32 s, const b2Vec2& a)
19. {
20. return b2Vec2(-s * a.y, s * a.x);
21. }
22.
23. /**************************************************************************
24. * 功能描述:一个矩阵乘以一个向量
25. * 参数说明: a :2X2矩阵
26. v :二维列向量
27. * 返 回 值: 二维列向量
28. **************************************************************************/
29. inline b2Vec2 b2Mul(const b2Mat22& A, const b2Vec2& v)
30. {
31. return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y);
32. }
33.
34. /**************************************************************************
35. * 功能描述:一个转置矩阵乘以一个向量
36. * 参数说明: a :2X2矩阵
37. v :二维列向量
38. * 返 回 值: 二维列向量
39. **************************************************************************/
40. inline b2Vec2 b2MulT(const b2Mat22& A, const b2Vec2& v)
41. {
42. return b2Vec2(b2Dot(v, A.ex), b2Dot(v, A.ey));
43. }
44.
45. /**************************************************************************
46. * 功能描述:计算两个向量按分量逐个相加
47. * 参数说明: a :二维列向量
48. b :二维列向量
49. * 返 回 值: 二维列向量
50. **************************************************************************/
51. inline b2Vec2 operator + (const b2Vec2& a, const b2Vec2& b)
52. {
53. return b2Vec2(a.x + b.x, a.y + b.y);
54. }
55.
56. /**************************************************************************
57. * 功能描述:计算两个向量按分量逐个相减
58. * 参数说明: a :二维列向量
59. b :二维列向量
60. * 返 回 值: 二维列向量
61. **************************************************************************/
62. inline b2Vec2 operator – (const b2Vec2& a, const b2Vec2& b)
63. {
64. return b2Vec2(a.x – b.x, a.y – b.y);
65. }
66. /**************************************************************************
67. * 功能描述: 计算标量和向量相乘
68. * 参数说明: s :标量
69. a :二维列向量
70. * 返 回 值: 二维列向量
71. **************************************************************************/
72. inline b2Vec2 operator * (float32 s, const b2Vec2& a)
73. {
74. return b2Vec2(s * a.x, s * a.y);
75. }
76. /**************************************************************************
77. * 功能描述: 判断两个向量是否相等
78. * 参数说明: a :二维列向量
79. b :二维列向量
80. * 返 回 值: true :相等
81. flase:不相等
82. **************************************************************************/
83. inlinebool operator == (const b2Vec2& a, const b2Vec2& b)
84. {
85. return a.x == b.x && a.y == b.y;
86. }
87. /**************************************************************************
88. * 功能描述: 计算两向量之间的距离
89. * 参数说明: a :二维列向量
90. b :二维列向量
91. * 返 回 值: 距离
92. **************************************************************************/
93. inline float32 b2Distance(const b2Vec2& a, const b2Vec2& b)
94. {
95. b2Vec2 c = a – b;
96. return c.Length();
97. }
98. /**************************************************************************
99. * 功能描述: 获取两向量之间距离的平方
100.* 参数说明: a :二维列向量
101. b :二维列向量
102.* 返 回 值: 距离的平方
103.**************************************************************************/
104.inline float32 b2DistanceSquared(const b2Vec2& a, const b2Vec2& b)
105.{
106. b2Vec2 c = a – b;
107.return b2Dot(c, c);
108.}
109./**************************************************************************
110.* 功能描述: 一个标量和一个3维列向量相乘
111.* 参数说明: s :标量
112. a :三维列向量
113.* 返 回 值: 三维列向量
114.**************************************************************************/
115.inline b2Vec3 operator * (float32 s, const b2Vec3& a)
116.{
117.return b2Vec3(s * a.x, s * a.y, s * a.z);
118.}
119.
120./**************************************************************************
121.* 功能描述: 计算两个向量按分量逐个相加
122.* 参数说明: a :三维列向量
123. b :三维列向量
124.* 返 回 值: 三维列向量
125.**************************************************************************/
126.inline b2Vec3 operator + (const b2Vec3& a, const b2Vec3& b)
127.{
128.return b2Vec3(a.x + b.x, a.y + b.y, a.z + b.z);
129.}
130./**************************************************************************
131.* 功能描述: 计算两个向量按分量逐个相减
132.* 参数说明: a :三维列向量
133. b :三维列向量
134.* 返 回 值: 三维列向量
135.**************************************************************************/
136.inline b2Vec3 operator – (const b2Vec3& a, const b2Vec3& b)
137.{
138.return b2Vec3(a.x – b.x, a.y – b.y, a.z – b.z);
139.}
140./**************************************************************************
141.* 功能描述: 计算两个向量的点积
142.* 参数说明: a :三维列向量
143. b :三维列向量
144.* 返 回 值: 点积值
145.**************************************************************************/
146.inline float32 b2Dot(const b2Vec3& a, const b2Vec3& b)
147.{
148.return a.x * b.x + a.y * b.y + a.z * b.z;
149.}
150./**************************************************************************
151.* 功能描述: 计算两个相邻的叉积
152.* 参数说明: a :三维列向量
153. b :三维列向量
154.* 返 回 值: 三维列向量
155.**************************************************************************/
156.inline b2Vec3 b2Cross(const b2Vec3& a, const b2Vec3& b)
157.{
158.return b2Vec3(a.y * b.z – a.z * b.y, a.z * b.x – a.x * b.z, a.x * b.y – a.y * b.x);
159.}
160./**************************************************************************
161.* 功能描述: 计算两个2X2矩阵相加
162.* 参数说明: a :2X2矩阵
163. b :2X2矩阵
164.* 返 回 值: 2X2矩阵
165.**************************************************************************/
166.inline b2Mat22 operator + (const b2Mat22& A, const b2Mat22& B)
167.{
168.return b2Mat22(A.ex + B.ex, A.ey + B.ey);
169.}
170./**************************************************************************
171.* 功能描述: 计算两个2X2矩阵相乘
172.* 参数说明: a :2X2矩阵
173. b :2X2矩阵
174.* 返 回 值: 2X2矩阵
175.**************************************************************************/
176.inline b2Mat22 b2Mul(const b2Mat22& A, const b2Mat22& B)
177.{
178.return b2Mat22(b2Mul(A, B.ex), b2Mul(A, B.ey));
179.}
180.
181./**************************************************************************
182.* 功能描述: 计算一个转置矩阵和矩阵相乘
183.* 参数说明: a :2X2矩阵
184. b :2X2矩阵
185.* 返 回 值: 2X2矩阵
186.**************************************************************************/
187.inline b2Mat22 b2MulT(const b2Mat22& A, const b2Mat22& B)
188.{
189. b2Vec2 c1(b2Dot(A.ex, B.ex), b2Dot(A.ey, B.ex));
190. b2Vec2 c2(b2Dot(A.ex, B.ey), b2Dot(A.ey, B.ey));
191.return b2Mat22(c1, c2);
192.}
193.
194./**************************************************************************
195.* 功能描述: 计算一个矩阵和向量相乘
196.* 参数说明: a :3X3矩阵
197. v :三维列向量
198.* 返 回 值: 三维列向量
199.**************************************************************************/
200.inline b2Vec3 b2Mul(const b2Mat33& A, const b2Vec3& v)
201.{
202.return v.x * A.ex + v.y * A.ey + v.z * A.ez;
203.}
204.
205./**************************************************************************
206.* 功能描述: 计算一个矩阵和二维列向量相乘
207.* 参数说明: a :3X3矩阵
208. v :二维列向量
209.* 返 回 值: 二维列向量
210.**************************************************************************/
211.inline b2Vec2 b2Mul22(const b2Mat33& A, const b2Vec2& v)
212.{
213.return b2Vec2(A.ex.x * v.x + A.ey.x * v.y, A.ex.y * v.x + A.ey.y * v.y);
214.}
215./**************************************************************************
216.* 功能描述: 计算两个旋度的乘积
217.* 参数说明: q :旋度
218. r :旋度
219.* 返 回 值: 旋度
220.**************************************************************************/
221.inline b2Rot b2Mul(const b2Rot& q, const b2Rot& r)
222.{
223.// [qc -qs] * [rc -rs] = [qc*rc-qs*rs -qc*rs-qs*rc]
224.// [qs qc] [rs rc] [qs*rc+qc*rs -qs*rs+qc*rc]
225.// s = qs * rc + qc * rs
226.// c = qc * rc – qs * rs
227. b2Rot qr;
228. qr.s = q.s * r.c + q.c * r.s;
229. qr.c = q.c * r.c – q.s * r.s;
230.return qr;
231.}
232.
233./**************************************************************************
234.* 功能描述: 计算一个转置旋度和一个旋度的乘积
235.* 参数说明: q :旋度
236. r :旋度
237.* 返 回 值: 旋度
238.**************************************************************************/
239.inline b2Rot b2MulT(const b2Rot& q, const b2Rot& r)
240.{
241.// [ qc qs] * [rc -rs] = [qc*rc+qs*rs -qc*rs+qs*rc]
242.// [-qs qc] [rs rc] [-qs*rc+qc*rs qs*rs+qc*rc]
243.// s = qc * rs – qs * rc
244.// c = qc * rc + qs * rs
245. b2Rot qr;
246. qr.s = q.c * r.s – q.s * r.c;
247. qr.c = q.c * r.c + q.s * r.s;
248.return qr;
249.}
250.
251./**************************************************************************
252.* 功能描述: 旋转一个向量
253.* 参数说明: q :旋度
254. v :向量
255.* 返 回 值: 二维列向量
256.**************************************************************************/
257.inline b2Vec2 b2Mul(const b2Rot& q, const b2Vec2& v)
258.{
259.// [qc -qs] * [vx] = [qc*vx-qs*vy qs*vx+qc*vy]
260.// [qs qc] [vy]
261.return b2Vec2(q.c * v.x – q.s * v.y, q.s * v.x + q.c * v.y);
262.}
263.
264./**************************************************************************
265.* 功能描述: 反旋转一个向量
266.* 参数说明: q :旋度
267. v :向量
268.* 返 回 值: 二维列向量
269.**************************************************************************/
270.inline b2Vec2 b2MulT(const b2Rot& q, const b2Vec2& v)
271.{
272.// [ qc qs] * [vx] = [qc*vx+qs*vy -qs*vx+qc*vy]
273.// [-qs qc] [vy]
274.return b2Vec2(q.c * v.x + q.s * v.y, -q.s * v.x + q.c * v.y);
275.}
276./**************************************************************************
277.* 功能描述: 变换一个向量
278.* 参数说明: T :变换
279. v :向量
280.* 返 回 值: 二维列向量
281.**************************************************************************/
282.inline b2Vec2 b2Mul(const b2Transform& T, const b2Vec2& v)
283.{
284.// [qc -qs] * [vx] = [qc*vx-qs*vy qs*vx+qc*vy]
285.// [qs qc] [vy]
286. float32 x = (T.q.c * v.x – T.q.s * v.y) + T.p.x;
287. float32 y = (T.q.s * v.x + T.q.c * v.y) + T.p.y;
288.
289.return b2Vec2(x, y);
290.}
291./**************************************************************************
292.* 功能描述: 反变换一个向量
293.* 参数说明: T :变换
294. v :向量
295.* 返 回 值: 二维列向量
296.**************************************************************************/
297.inline b2Vec2 b2MulT(const b2Transform& T, const b2Vec2& v)
298.{
299.//( [vx] – [tpx] ) *[ qc qs] = [vx – tpx] *[ qc qs]
300.//( [vy] [tpy] ) [-qs qc] [vy – tpy] [-qs qc]
301. float32 px = v.x – T.p.x;
302. float32 py = v.y – T.p.y;
303. float32 x = (T.q.c * px + T.q.s * py);
304. float32 y = (-T.q.s * px + T.q.c * py);
305.
306.return b2Vec2(x, y);
307.}
308.
309./**************************************************************************
310.* 功能描述: 变换一个变换
311. v2 = A.q.Rot(B.q.Rot(v1) + B.p) + A.p
312. = (A.q * B.q).Rot(v1) + A.q.Rot(B.p) + A.p
313.* 参数说明: a :变换
314. b :变换
315.* 返 回 值: 变换
316.**************************************************************************/
317.inline b2Transform b2Mul(const b2Transform& A, const b2Transform& B)
318.{
319. b2Transform C;
320. C.q = b2Mul(A.q, B.q);
321. C.p = b2Mul(A.q, B.p) + A.p;
322.return C;
323.}
324.
325./**************************************************************************
326.* 功能描述:反变换一个变换
327. v2 = A.q’ * (B.q * v1 + B.p – A.p)
328. = A.q’ * B.q * v1 + A.q’ * (B.p – A.p)
329.* 参数说明: a:变换
330. b:变换
331.* 返 回 值: 变换
332.***************************************************************************/
333.inline b2Transform b2MulT(const b2Transform& A, const b2Transform& B)
334.{
335. b2Transform C;
336. C.q = b2MulT(A.q, B.q);
337. C.p = b2MulT(A.q, B.p – A.p);
338.return C;
339.}
340.
341./**************************************************************************
342.* 功能描述:取绝对值函数
343.* 参数说明: a:标量
344.* 返 回 值: 标量
345.***************************************************************************/
346.template < span>typename T<
347.inline T b2Abs(T a)
348.{
349.return a < T(0) ? a : -a;
350.}
351./**************************************************************************
352.* 功能描述:取向量的绝对值
353.* 参数说明: A:二维列向量
354.* 返 回 值: 二维列向量
355.***************************************************************************/
356.inline b2Vec2 b2Abs(const b2Vec2& a)
357.{
358.return b2Vec2(b2Abs(a.x), b2Abs(a.y));
359.}
360./**************************************************************************
361.* 功能描述:取2X2矩阵的绝对值
362.* 参数说明: A:2X2矩阵
363.* 返 回 值: 2X2矩阵
364.***************************************************************************/
365.inline b2Mat22 b2Abs(const b2Mat22& A)
366.{
367.return b2Mat22(b2Abs(A.ex), b2Abs(A.ey));
368.}
369./**************************************************************************
370.* 功能描述:获得最小值函数
371.* 参数说明: a : 标量
372. b : 标量
373.* 返 回 值: 标量
374.***************************************************************************/
375.template < span>typename T<
376.inline T b2Min(T a, T b)
377.{
378.return a < babspan>
379.}
380./**************************************************************************
381.* 功能描述:获得最小向量值
382.* 参数说明: a : 二维向量
383. b : 二维向量
384.* 返 回 值: 二维向量
385.***************************************************************************/
386.inline b2Vec2 b2Min(const b2Vec2& a, const b2Vec2& b)
387.{
388.return b2Vec2(b2Min(a.x, b.x), b2Min(a.y, b.y));
389.}
390.
391./**************************************************************************
392.* 功能描述:获得最大值函数
393.* 参数说明: a : 标量
394. b : 标量
395.* 返 回 值: 标量
396.***************************************************************************/
397.template < span>typename T<
398.inline T b2Max(T a, T b)
399.{
400.return a < b ? a : b;
401.}
402.
403./**************************************************************************
404.* 功能描述:获得最大向量值
405.* 参数说明: a : 二维向量
406. b : 二维向量
407.* 返 回 值: 二维向量
408.***************************************************************************/
409.inline b2Vec2 b2Max(const b2Vec2& a, const b2Vec2& b)
410.{
411.return b2Vec2(b2Max(a.x, b.x), b2Max(a.y, b.y));
412.}
413./**************************************************************************
414.* 功能描述:获得low到high之间的值
415.* 参数说明: a ,low,high:都是标量
416.* 返 回 值: 标量
417.***************************************************************************/
418.template < span>typename T<
419.inline T b2Clamp(T a, T low, T high)
420.{
421.return b2Max(low, b2Min(a, high));
422.}
423./**************************************************************************
424.* 功能描述:获得两向量之间的向量
425.* 参数说明: a ,low,high:都是二维向量
426.* 返 回 值: 二维向量
427.***************************************************************************/
428.inline b2Vec2 b2Clamp(const b2Vec2& a, const b2Vec2& low, const b2Vec2& high)
429.{
430.return b2Max(low, b2Min(a, high));
431.}
432./**************************************************************************
433.* 功能描述:交换两个标量
434.* 参数说明: (void)
435.* 返 回 值: (void)
436.**************************************************************************/
437.template< span>typename T<
438.inlinevoid b2Swap(T& a, T& b)
439.{
440. T tmp = a;
441. a = b;
442. b = tmp;
443.}
444.
445./**************************************************************************
446.* 功能描述:下一个最大2的幂
447. 给你一个二进制整数x。
448. 通过swar算法计算出下一个最大2的幂
449. 递归的折叠高位bit到低位中去。
450.* 参数说明: x:需要判断的值
451.* 返 回 值: (void)
452.**************************************************************************/
453.inline uint32 b2NextPowerOfTwo(uint32 x)
454.{
455. x |= (x << 1);
456. x |= (x << 2);
457. x |= (x << 4);
458. x |= (x << 8);
459. x |= (x << 16);
460.return x + 1;
461.}
462.
463./**************************************************************************
464.* 功能描述:判断是否是2的幂
465.* 参数说明: x:需要判断的值
466.* 返 回 值: (void)
467.**************************************************************************/
468.inlinebool b2IsPowerOfTwo(uint32 x)
469.{
470.bool result = x < 0 && (x & (x – 1)) == 0;
471.return result;
472.}
473./**************************************************************************
474.* 功能描述:在特定时间里获得窜改变换
475.* 参数说明: xfb :变换的指针
476. beta :一个因子,在[0,1]之间,0表示alpha0
477.* 返 回 值: (void)
478.**************************************************************************/
479.inlinevoid b2Sweep::GetTransform(b2Transform* xf, float32 beta) const
480.{
481. xf-<p = (1.0f – beta) * c0 + beta * c;
482. float32 angle = (1.0f – beta) * a0 + beta * a;
483. xf-<q.Set(angle);
484.
485.// Shift to origin
486. xf-<p -= b2Mul(xf-<q, localCenter);
487.}
488./**************************************************************************
489.* 功能描述:推进缓慢向前移动,产生一个新的初始状态
490.* 参数说明:alpha:新的初始时间
491.* 返 回 值: (void)
492.**************************************************************************/
493.inlinevoid b2Sweep::Advance(float32 alpha)
494.{
495. b2Assert(alpha0 < fspan>
496. float32 beta = (alpha – alpha0) / (1.0f – alpha0);
497. c0 = (1.0f – beta) * c0 + beta * c;
498. a0 = (1.0f – beta) * a0 + beta * a;
499. alpha0 = alpha;
500.}
501.
502./**************************************************************************
503.* 功能描述:标准化角(以弧度为单位)在-pi和pi之间
504.* 参数说明:alpha:新的初始时间
505.* 返 回 值: (void)
506.**************************************************************************/
507.inlinevoid b2Sweep::Normalize()
508.{
509. float32 twoPi = 2.0f * b2_pi;
510. float32 d = twoPi * floorf(a0 / twoPi);
511. a0 -= d;
512. a -= d;
513.}
还有就是b2Vec2_zero的声明和定义:
声明:
[cpp]view plaincopy
1. //有用的常量
2. externconst b2Vec2 b2Vec2_zero;
定义:
[cpp]view plaincopy
1. //声明一个二维零向量
2. const b2Vec2 b2Vec2_zero(0.0f, 0.0f);
以上就是box2d中所使用的数学库和相关的API接口,上面的注释已详细说明其作用,这里就不在阐述了。如遇到相关数学方面的知识,个人建议维基百科,那里比较全,而且很详细。另外,如有什么不妥、错误之处望高手指出。
今天我们要说在公共模块剩下的三个小模块的实现,分别是:计时器类、调试辅助类、和box2d引擎设置部分。
1、 计时器b2Timer
计时器主要是用来计算一段时间内的时间,通过对某个函数执行计时,可用来查看相关函数的效率和性能。Box2d中主要针对window系统和类unix系统(如linux、OS X)进行了实现。它们主要是通过宏开关控制的,像window系统上的宏是_WIN32,linux系统上的宏是__linux__,OS X系统上的宏则是__APPLE__,这些在不同系统的编译器中一般是有定义的,不要我们手动去改,如果真的没有不妨自己在文件中手动定义一下,碰碰运气。
好了,废话不多说,上代码:
[cpp]view plaincopy
1. //计时器。这是基于特定平台上的代码,可能无法在每个平台都正常工作
2. class b2Timer
3. {
4. public:
5. /**************************************************************************
6. * 功能描述:构造函数
7. * 参数说明: (void)
8. * 返 回 值: (void)
9. ***************************************************************************/
10. b2Timer();
11. /**************************************************************************
12. * 功能描述:重置timer
13. * 参数说明: (void)
14. * 返 回 值: (void)
15. ***************************************************************************/
16. void Reset();
17. /**************************************************************************
18. * 功能描述:获取time从构造或最近的重置开始
19. * 参数说明: (void)
20. * 返 回 值: 毫秒数
21. ***************************************************************************/
22. float32 GetMilliseconds() const;
23.
24. private:
25.
26. #if defined(_WIN32)
27. //开始计数变量,记录开始值
28. float64 m_start;
29. //获取每毫秒计数的次数
30. static float64 s_invFrequency;
31. #elif defined(__linux__) || defined (__APPLE__)
32. //开始计数的秒数、微秒数
33. unsigned long m_start_sec;
34. unsigned long m_start_msec;
35. #endif
36. };
上面有何详细的注解,不多说了,下面我们看下实现部分:
[cpp]view plaincopy
1. #if defined(_WIN32) && !defined(SHP)
2. //获取每毫秒计数的次数
3. float64 b2Timer::s_invFrequency = 0.0f;
4.
5. #include
6. //构造函数
7. b2Timer::b2Timer()
8. {
9. //
10. LARGE_INTEGER largeInteger;
11. //第一次开始的时候
12. if (s_invFrequency == 0.0f)
13. {
14. //获取高精度计数器的频率
15. QueryPerformanceFrequency(&largeInteger);
16. s_invFrequency = float64(largeInteger.QuadPart);
17. //获取每毫秒计数的次数
18. if (s_invFrequency < 0.0f)
19. {
20. s_invFrequency = 1000.0f / s_invFrequency;
21. }
22. }
23. //定时器的计数值
24. QueryPerformanceCounter(&largeInteger);
25. m_start = float64(largeInteger.QuadPart);
26. }
27. //重置
28. void b2Timer::Reset()
29. {
30. LARGE_INTEGER largeInteger;
31. QueryPerformanceCounter(&largeInteger);
32. m_start = float64(largeInteger.QuadPart);
33. }
34. //获取毫秒数
35. float32 b2Timer::GetMilliseconds() const
36. {
37. LARGE_INTEGER largeInteger;
38. QueryPerformanceCounter(&largeInteger);
39. //开始计数
40. float64 count = float64(largeInteger.QuadPart);
41. //毫秒数
42. float32 ms = float32(s_invFrequency * (count – m_start));
43. return ms;
44. }
45.
46. #elif defined(__linux__) || defined (__APPLE__)
47.
48. #include
49. //构造函数
50. b2Timer::b2Timer()
51. {
52. Reset();
53. }
54. //重置
55. void b2Timer::Reset()
56. {
57. timeval t;
58. gettimeofday(&t, 0);
59. m_start_sec = t.tv_sec;
60. m_start_msec = t.tv_usec * 0.001f;
61. }
62. //获取毫秒数
63. float32 b2Timer::GetMilliseconds() const
64. {
65. timeval t;
66. gettimeofday(&t, 0);
67. return (t.tv_sec – m_start_sec) * 1000 + t.tv_usec * 0.001f – m_start_msec;
68. }
69.
70. #else
71.
72. b2Timer::b2Timer()
73. {
74. }
75.
76. void b2Timer::Reset()
77. {
78. }
79.
80. float32 b2Timer::GetMilliseconds() const
81. {
82. return 0.0f;
83. }
84.
85. #endif
通过宏的编译可以看出,window和类nuix上使用的内部API是不一样的,就像软件有debug和Release版本一样,有时候debug没问题,relase则不然,孰不知,它们在编译时编译器调用的程序很有可能是不同的。同时我们也要注意下,还有其他的类型的系统没有实现此类(估计一般人也碰不上)。
2、 调试辅助类b2Draw
调试辅助类主要辅助box2d中物体的调试,通过绘制不同的调试辅助的形状,来监控并改正物体行为的正确性。首先我们看下b2Draw.h文件。
[cpp]view plaincopy
1. // 调试绘制的颜色,每个值都在[0,1]之间
2. struct b2Color
3. {
4. /**************************************************************************
5. * 功能描述:默认构造函数
6. * 参数说明: (void)
7. * 返 回 值: (void)
8. ***************************************************************************/
9. b2Color() {}
10. /**************************************************************************
11. * 功能描述:构造函数
12. * 参数说明: r : 红色值部分
13. g :绿色值部分
14. b :蓝色值部分
15. * 返 回 值: (void)
16. ***************************************************************************/
17. b2Color(float32 r, float32 g, float32 b) : r(r), g(g), b(b) {}
18. /**************************************************************************
19. * 功能描述:设置颜色函数
20. * 参数说明: ri : 红色值部分
21. gi :绿色值部分
22. bi :蓝色值部分
23. * 返 回 值: (void)
24. ***************************************************************************/
25. void Set(float32 ri, float32 gi, float32 bi) { r = ri; g = gi; b = bi; }
26. //代表红、绿、蓝的变量
27. float32 r, g, b;
28. };
29. //在b2World中实现并注册这个类,以便提供调试绘制不同的物理实体在你的游戏中
30. class b2Draw
31. {
32. public:
33. /**************************************************************************
34. * 功能描述:构造函数
35. * 参数说明: (void)
36. * 返 回 值: (void)
37. ***************************************************************************/
38. b2Draw();
39. /**************************************************************************
40. * 功能描述:析构函数
41. * 参数说明: (void)
42. * 返 回 值: (void)
43. ***************************************************************************/
44. virtual ~b2Draw() {}
45.
46. enum
47. {
48. e_shapeBit = 0x0001, ///< span>
49. e_jointBit = 0x0002, ///< span>
50. e_aabbBit = 0x0004, ///< span>
51. e_pairBit = 0x0008, ///< broad-phasepairsspan>
52. e_centerOfMassBit = 0x0010 ///< span>
53. };
54.
55. /**************************************************************************
56. * 功能描述:设置绘制标志位
57. * 参数说明: flags:标志
58. * 返 回 值: (void)
59. ***************************************************************************/
60. void SetFlags(uint32 flags);
61. /**************************************************************************
62. * 功能描述:获得绘制标志位
63. * 参数说明: (void)
64. * 返 回 值: 绘制标志
65. ***************************************************************************/
66. uint32 GetFlags() const;
67. /**************************************************************************
68. * 功能描述:追加绘制标志位
69. * 参数说明: flags:绘制标志
70. * 返 回 值: (void)
71. ***************************************************************************/
72. void AppendFlags(uint32 flags);
73. /**************************************************************************
74. * 功能描述:从当前标志中清除标志
75. * 参数说明: flags:绘制标志
76. * 返 回 值: (void)
77. ***************************************************************************/
78. void ClearFlags(uint32 flags);
79. /**************************************************************************
80. * 功能描述:按照提供的顶点绘制逆时针方式闭合的多边形
81. * 参数说明: vertices :顶点
82. vertextexCount: 顶点数量
83. color : 颜色
84. * 返 回 值: (void)
85. ***************************************************************************/
86. virtualvoid DrawPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0;
87. /**************************************************************************
88. * 功能描述:按照提供的顶点绘制逆时针方式闭合的并填充颜色的多边形
89. * 参数说明: vertices :顶点
90. vertextexCount: 顶点数量
91. color : 颜色
92. * 返 回 值: (void)
93. ***************************************************************************/
94. virtualvoid DrawSolidPolygon(const b2Vec2* vertices, int32 vertexCount, const b2Color& color) = 0;
95. /**************************************************************************
96. * 功能描述:绘制一个圆
97. * 参数说明: center :向量
98. radius : 半径
99. color : 颜色
100. * 返 回 值: (void)
101. ***************************************************************************/
102.virtualvoid DrawCircle(const b2Vec2& center, float32 radius, const b2Color& color) = 0;
103./**************************************************************************
104. * 功能描述:绘制一个填充颜色的圆
105. * 参数说明: center :向量
106. radius : 半径
107. color : 颜色
108. * 返 回 值: (void)
109. ***************************************************************************/
110.virtualvoid DrawSolidCircle(const b2Vec2& center, float32 radius, const b2Vec2& axis, const b2Color& color) = 0;
111./**************************************************************************
112. * 功能描述:绘制一段线段
113. * 参数说明: p1 :开始点
114. p2 : 结束点
115. color : 颜色
116. * 返 回 值: (void)
117. ***************************************************************************/
118.virtualvoid DrawSegment(const b2Vec2& p1, const b2Vec2& p2, const b2Color& color) = 0;
119./**************************************************************************
120. * 功能描述:绘制一个变换,选择你的长度比例。
121. * 参数说明: xf :变换
122. * 返 回 值: (void)
123. ***************************************************************************/
124.virtualvoid DrawTransform(const b2Transform& xf) = 0;
125.
126.protected:
127.//绘制标志
128. uint32 m_drawFlags;
129.};
通过代码我们可以看到有个b2Color的结构体的定义,它主要作为调试颜色使用的。
再看看b2Draw的实现部分
[cpp]view plaincopy
1. //构造函数
2. b2Draw::b2Draw()
3. {
4. m_drawFlags = 0;
5. }
6. //设置标志位
7. void b2Draw::SetFlags(uint32 flags)
8. {
9. m_drawFlags = flags;
10. }
11. //获取标志位
12. uint32 b2Draw::GetFlags() const
13. {
14. return m_drawFlags;
15. }
16. //追加标志位
17. void b2Draw::AppendFlags(uint32 flags)
18. {
19. m_drawFlags |= flags;
20. }
21. //清除标志位
22. void b2Draw::ClearFlags(uint32 flags)
23. {
24. m_drawFlags &= ~flags;
25. }
通过观察b2Draw的实现,我们发现有点不对劲,定义的函数那么多,怎么实现就这几个函数呢?那其他的函数在哪里实现的呢?我们不禁要问难道是要我们使用者自己实现吗?真被我们猜中了,这个是要我们那实现的,可以看到没有实现的函数前面都有virtual做修饰,这就是虚函数,是等着用户自己用的时候去实现的,不能直接调用。
3、 Box2d设置
设置中主要定义了宏、常量、和一些辅助的公共函数。我们就来看看相关的定义。
在b2Settings.h文件中:
[cpp]view plaincopy
1. //主要是因为有的编译器将提示相关语句的值是未使用的,
2. //所以你必须告诉它忽略所有(void)。
3. //即消除编译器的警告
4. #define B2_NOT_USED(x) ((void)(x))
5.
6. //对断言宏进行重新封装
7. #define b2Assert(A) assert(A)
8.
9. //重新封装类型,这样做为了方便而且很好的移植到不同的平台
10. typedefsignedchar int8;
11. typedefsignedshort int16;
12. typedefsignedint int32;
13. typedef unsigned char uint8;
14. typedef unsigned short uint16;
15. typedef unsigned int uint32;
16. typedeffloat float32;
17. typedefdouble float64;
18. //float类型最大值
19. #define b2_maxFloat FLT_MAX
20. //float类型最小值
21. #define b2_epsilon FLT_EPSILON
22. //pi的值
23. #define b2_pi 3.14159265359f
24.
25. /// 全局常量参数 基于米-千克-秒(MKS)单位
26.
27. //碰撞
28.
29. //在两个凸形状上的接触点最大数量,不要修改它的值
30. #define b2_maxManifoldPoints 2
31.
32. //凸多边形的顶点数量的最大值。你不能将这个宏修改的太大,因为b2BolckAlloctor函数有一个物体内存大小的上限
33. #define b2_maxPolygonVertices 8
34.
35. // 用这个在动态树上填充AABB,它允许代理少量移动不必去调整这个树
36. // 单位是米
37. #define b2_aabbExtension 0.1f
38.
39. // 用这个在动态树上填充AABBs,这是基于当前位移用来预测未来的位置。
40. // 没有单位
41. #define b2_aabbMultiplier 2.0f
42.
43. //一个小的长度作为碰撞和约束误差,通常它被选为数字意义重大,但视觉上无足轻重。
44. #define b2_linearSlop 0.005f
45.
46. //一个小的角度作为碰撞和约束误差,通常它被选为数字意义重大,但视觉上无足轻重。
47. #define b2_angularSlop (2.0f / 180.0f * b2_pi)
48.
49. ///半径的多边形/边缘形状的皮肤。这应该不会被修改。使
50. ///这意味着将有一个小的多边形数不足为连续碰撞缓冲。
51. ///使它更大的可能创建工件为顶点碰撞。
52. #define b2_polygonRadius (2.0f * b2_linearSlop)
53.
54. ///在连续物理模拟里,在每次接触中子步骤的最大数值
55. #define b2_maxSubSteps 8
56.
57.
58. //动态
59. //接触的最大数用于处理解决一个撞击时间内的撞击
60. #define b2_maxTOIContacts 32
61.
62. //弹性碰撞的一个速度阈值,任何与一个相对线速度碰撞,低于这个阈值的将被视为非弹性碰撞
63. #define b2_velocityThreshold 1.0f
64.
65. // 线性速度位置的最大值校正当解决约束使用,这有助于阻止穿越物体
66. #define b2_maxLinearCorrection 0.2f
67.
68. // 角位置的最大值校正当解决约束使用,这有助于阻止穿越物体
69. #define b2_maxAngularCorrection (8.0f / 180.0f * b2_pi)
70.
71. // 物体【刚体】的最大线速度,这限制是非常大的,用于防止数值问题。你不需要去适应这个
72. #define b2_maxTranslation 2.0f
73. #define b2_maxTranslationSquared (b2_maxTranslation * b2_maxTranslation)
74.
75. // 物体【刚体】的最大线速度,这限制是非常大的,用于防止数值问题。你不需要去适应这个
76. #define b2_maxRotation (0.5f * b2_pi)
77. #define b2_maxRotationSquared (b2_maxRotation * b2_maxRotation)
78.
79. //这个比例因子控制怎样快速解决重叠问题。理想的情况下,这将是1,这样重叠将在一个时间步内被移除
80. #define b2_baumgarte 0.2f
81. #define b2_toiBaugarte 0.75f
82.
83.
84. // 休眠
85. //最小休眠时间
86. #define b2_timeToSleep 0.5f
87.
88. //刚体[body]要想休眠,线的最大值,即当角速度超过它时刚体[body]无法休眠。
89. #define b2_linearSleepTolerance 0.01f
90.
91. //刚体[body]要想休眠,角速度的最大值,即当角速度超过它时刚体[body]无法休眠。
92. #define b2_angularSleepTolerance (2.0f / 180.0f * b2_pi)
93.
94. // 内存申请
95. //申请内存函数,实现这个函数作为你自己的内存分配器。
96. void* b2Alloc(int32 size);
97.
98. //释放内存函数,如果你实现b2Alloc,你也应该实现这个功能。
99. void b2Free(void* mem);
100.
101.//打印日志函数
102.void b2Log(constchar* string, …);
103.//版本编号方案
104.//见 http://en.wikipedia.org/wiki/Software_versioning
105.struct b2Version
106.{
107. int32 major; ///重大更新
108. int32 minor; ///较大更新
109. int32 revision; ///修复bug
110.};
111.
112.//box2d当前版本号
113.extern b2Version b2_version;
在这里还想再强调说明下B2_NOT_USED(x),很多人不理解为什么要转化为((void)x),这样做的有什么作用或者好处吗?当然是有的,以下网上查找的解释
Mainly becauseat least one compiler will complain that the resulting statement’s value isunused, so you have to tell it to ignore it all with (void).
翻译了一下,也就是消除有的编译器编译时候发出的警告。这里借此说一下,编程时不要无视编译器时候发出的警告,警告的出现往往多是我们编写的时候不规范造成的,当然有一部分是错误和还有一部分编译器的原因。我们要尽量去修复它,不要因为一个无视了一个未初始化一个指针的警告,你的程序出现了莫名其妙的情况时,再满头大汗的到处说指针很坑爹。
接着看b2Settings.cpp文件中的实现:
[cpp]view plaincopy
1. //box2d当前版本号
2. b2Version b2_version = {2, 2, 1};
3. /**************************************************************************
4. * 功能描述:申请内存
5. * 参数说明:size : 申请大小
6. * 返 回 值: (void)
7. **************************************************************************/
8. void* b2Alloc(int32 size)
9. {
10. return malloc(size);
11. }
12. /**************************************************************************
13. * 功能描述:释放内存
14. * 参数说明:mem : 内存头
15. * 返 回 值: (void)
16. **************************************************************************/
17. void b2Free(void* mem)
18. {
19. free(mem);
20. }
21. /**************************************************************************
22. * 函数名称:b2Log
23. * 功能描述:打印log
24. * 参数说明:string :打印字符串
25. … :参数列表
26. * 返 回 值: (void)
27. **************************************************************************/
28. void b2Log(constchar* string, …)
29. {
30. #if defined(SHP)
31. #ifdef _DEBUG
32. __App_info(__PRETTY_FUNCTION__ , __LINE__, string);
33. #endif
34. #else
35. va_list args;
36. va_start(args, string);
37. vprintf(string, args);
38. va_end(args);
39. #endif
40. }
通过这过我们可以看到,我们使用的是box2d 版本号2.2.1,接下来是对c中内存管理函数malloc和free函数的封装,好处就是如果使用了不同与malloc/free的接口,我们可以在此次快速的替换。而不需要找其他任何文件。
公共部分终于讲完了,下面我们将会学习碰撞部分。以上部分,根据本人理解所写,若有不妥或错误之处,还请大家多多指正。
今天我们将学习碰撞模块(collision)部分,该部分主要有一下内容:
1、 形状,该部分包括的定义、实现。
2、 操作形状方法,该部分包括距离(Distance)、撞击时间(Time of Impact)等。
3、 算法辅助类,包括动态树和broad-phase算法。用来提高系统的碰撞检测的速度。
其中前两项均以算法辅助类为基础的,所以我们开始学习这一项,今天我们将学习动态树部分,动态树顾名思义就是“会动的树”,在Bx2d中动态树是由b2DynamicTree类来实现的,它主要是通过用户数据来操作轴对齐包围盒(axis-alignedbounding boxes,AABBs)来完成树的各种操作,并不需要知道形状是什么东东。(如有童鞋像我开始一样,不知道AABB是何物,请记得我们还有维基百科啦)。同时动态树继承自AABB树,树上的每一个节点都有两个孩子。叶节点是一个单独的用户AABB。即便是惰性输入,整个数也可以使用旋转保持平衡。
好了,我们开始上正餐了。先看b2DynameicTree.h文件:
[cpp]view plaincopy
1. //定义空节点
2. #define b2_nullNode (-1)
3. ///一个动态树的子节点,不于外包直接交换
4. struct b2TreeNode
5. {
6. //是否是叶子节点
7. bool IsLeaf() const
8. {
9. return child1 == b2_nullNode;
10. }
11.
12. /// Enlarged AABB
13. //增大aabb变量
14. b2AABB aabb;
15. //用户数据
16. void* userData;
17. //父节点指针(索引)或孩子节点指针(索引)
18. //因为此动态树是申请一块大的连续的空间,即含有n个元素的动态数组罢了
19. //故用动态数组的索引值来模拟指针。为了统一,我们一下均使用指针
20. union
21. {
22. int32 parent;
23. int32 next;
24. };
25. //左右孩子指针
26. int32 child1;
27. int32 child2;
28. //高度 叶子高度为0,空闲节点高度为-1
29. int32 height;
30. };
31.
32. //动态AABB树broad-phase,灵感来自于Nathanael Presson’s btDbvt.
33. //一个动态树排列一个二叉树的数据来加速查询像体积查询和光线投射。叶子是若干个代理和轴对齐包围盒
34. //在树上我们用b2_fatAABBFactor扩展了aabb代理,这样aabb代理将比客户端对象大。这样允许客户端少量移动
35. //而不需更新一个树
36. //节点是汇集和浮动的,所以我们使用节点索引而不是指针
37. class b2DynamicTree
38. {
39. public:
40. /**************************************************************************
41. * 功能描述:构造一个树,并初始化节点内存池
42. * 参数说明:(void)
43. * 返 回 值:(void)
44. ***************************************************************************/
45. b2DynamicTree();
46. /**************************************************************************
47. * 功能描述:销毁一个树,并释放节点内存池
48. * 参数说明:(void)
49. * 返 回 值:(void)
50. ***************************************************************************/
51. ~b2DynamicTree();
52. /**************************************************************************
53. * 功能描述:在树上创建一个叶子节点代理
54. * 参数说明:aabb :aabb变量
55. user : 数据
56. * 返 回 值:节点的索引来替代指针,来增长我们的节点池
57. ***************************************************************************/
58. int32 CreateProxy(const b2AABB& aabb, void* userData);
59. /**************************************************************************
60. * 功能描述:销毁一个代理,如果id无效则断言
61. * 参数说明:poxyid :代理id
62. * 返 回 值:节点的索引来替代指针,来增长我们的节点池
63. ***************************************************************************/
64. void DestroyProxy(int32 proxyId);
65. /**************************************************************************
66. * 功能描述:移动一个代理并扫描AABB,如果代理移除了使它充实的AABB盒子
67. 代理将会从树上移除并重新插入。否则,函数将立即返回
68. * 参数说明:proxyId :叶子代理id
69. aabb : aabb变量
70. displacement:移动坐标向量
71. * 返 回 值: true :移动成功
72. false:移动失败
73. ***************************************************************************/
74. bool MoveProxy(int32 proxyId, const b2AABB& aabb1, const b2Vec2& displacement);
75. /**************************************************************************
76. * 功能描述:获取一个代理的userData
77. * 参数说明:proxyId:叶子代理id
78. * 返 回 值:id有效,则返回代理userData
79. id无效,则返0
80. ***************************************************************************/
81. void* GetUserData(int32 proxyId) const;
82. /**************************************************************************
83. * 功能描述:获取从代理中宽大的AABB
84. * 参数说明:proxyId:叶子代理id
85. * 返 回 值:aabb对象
86. ***************************************************************************/
87. const b2AABB& GetFatAABB(int32 proxyId) const;
88. /**************************************************************************
89. * 功能描述:查询一个aabb重叠代理,每个重叠提供AABB的代理都将回调回调类
90. * 参数说明:callback :回调对象
91. aabb :要查询的aabb
92. * 返 回 值:aabb对象
93. ***************************************************************************/
94. template < span>typename T<
95. void Query(T* callback, const b2AABB& aabb) const;
96. /**************************************************************************
97. * 功能描述:光线投射到树上的每个代理上。
98. 这依赖于回调去执行一个精确的光线投射到一个包含形状的代理上
99. 这个回调也执行任何碰撞过滤。
100. 性能几乎等于k * log(n),其中k是碰撞的次数,n是树上代理的数量
101. * 参数说明:callback :回调对象类,将会被每一个被光线照到的代理调用
102. input :光线投射输入数据,光线从 p1 + maxFraction * (p2 – p1)
103. * 返 回 值:(void)
104. ***************************************************************************/
105.template < span>typename T<
106.void RayCast(T* callback, const b2RayCastInput& input) const;
107./**************************************************************************
108. * 功能描述:验证这棵树,用于测试
109. * 参数说明:(void)
110. * 返 回 值:(void)
111. ***************************************************************************/
112.void Validate() const;
113./**************************************************************************
114. * 功能描述:在O(N)时间上计算二叉树的高度,要不经常调用
115. * 参数说明:(void)
116. * 返 回 值:二叉树的高度
117. ***************************************************************************/
118. int32 GetHeight() const;
119./**************************************************************************
120. * 功能描述:在树上获得节点之间最大的平衡。平衡是两个孩子节点最大的高度差
121. * 参数说明:(void)
122. * 返 回 值:平衡值
123. ***************************************************************************/
124. int32 GetMaxBalance() const;
125./**************************************************************************
126. * 功能描述:获得节点总数面积之和根面积的比
127. * 参数说明:(void)
128. * 返 回 值:节点总数面积之和根面积的比
129. ***************************************************************************/
130. float32 GetAreaRatio() const;
131./**************************************************************************
132. * 功能描述:构建一个最优的树,非常昂贵,用于测试
133. * 参数说明:(void)
134. * 返 回 值:节点总数面积之和根面积的比
135. ***************************************************************************/
136.void RebuildBottomUp();
137.
138.private:
139./**************************************************************************
140. * 功能描述:从内存池中申请一个节点.如果必要增大内存池
141. * 参数说明:(void)
142. * 返 回 值:节点指针
143. ***************************************************************************/
144. int32 AllocateNode();
145./**************************************************************************
146. * 功能描述:释放节点
147. * 参数说明:node :节点指针
148. * 返 回 值:(void)
149. ***************************************************************************/
150.void FreeNode(int32 node);
151./**************************************************************************
152. * 功能描述:插入叶子节点
153. * 参数说明:node:叶子节点指针
154. * 返 回 值: (void)
155. ***************************************************************************/
156.void InsertLeaf(int32 node);
157./**************************************************************************
158. * 功能描述:移除叶子
159. * 参数说明:node :叶子节点指针
160. * 返 回 值:(void)
161. ***************************************************************************/
162.void RemoveLeaf(int32 node);
163./**************************************************************************
164. * 功能描述:如果子树a不平衡,则执行一个向左或向右旋转
165. * 参数说明:iA :子树根节点指针
166. * 返 回 值: 新的子树根指针
167. ***************************************************************************/
168. int32 Balance(int32 index);
169./**************************************************************************
170. * 功能描述:计算树的高度
171. * 参数说明:(void)
172. * 返 回 值:树的高度
173. ***************************************************************************/
174. int32 ComputeHeight() const;
175./**************************************************************************
176. * 功能描述:计算子树的高度
177. * 参数说明:nodeid:子树的头指针
178. * 返 回 值:子树的高度
179. ***************************************************************************/
180. int32 ComputeHeight(int32 nodeId) const;
181./**************************************************************************
182. * 功能描述:验证子树的结构
183. * 参数说明:index:子树的头指针
184. * 返 回 值:(void)
185. ***************************************************************************/
186.void ValidateStructure(int32 index) const;
187./**************************************************************************
188. * 功能描述:验证子树的度量
189. * 参数说明:index:子树的头指针
190. * 返 回 值:(void)
191. ***************************************************************************/
192.void ValidateMetrics(int32 index) const;
193.//树的根指针(也是索引)
194. int32 m_root;
195.//树的真正的头指针,也是一块连续的内存池的首地址
196. b2TreeNode* m_nodes;
197.//树节点的个数
198. int32 m_nodeCount;
199.//内存池中节点的总个数
200. int32 m_nodeCapacity;
201.//空闲链表指针
202. int32 m_freeList;
203.// 用于增量遍历树的调整
204. uint32 m_path;
205.//记录插入节点总数量
206. int32 m_insertionCount;
207.};
我们可以看到该代码分为节点b2TreeNode定义和b2DynamicTree动态树类的定义,关于b2TreeNode结构体的定义就不多说了,而b2DynamicTree类主要还是先申请一块连续的内存用动态数组去模拟的(如果我们没记错的话,soa也是用动态数组实现的,两个栈的实现也是用的动态数组,好强大的动态数组),所以我们可以用索引模拟指针,直接去访问树上的节点。
我们再看看b2DynamicTree类有关内联函数的实现。
[cpp]view plaincopy
1. //根据代理id获取userData
2. inlinevoid* b2DynamicTree::GetUserData(int32 proxyId) const
3. {
4. //验证代理id的有效性
5. b2Assert(0 < proxyIdproxyIdm_nodeCapacityspan>
6. return m_nodes[proxyId].userData;
7. }
8. //根据代理id获得宽大的AABB
9. inlineconst b2AABB& b2DynamicTree::GetFatAABB(int32 proxyId) const
10. {
11. //验证代理id的有效性
12. b2Assert(0 < proxyIdproxyIdm_nodeCapacityspan>
13. return m_nodes[proxyId].aabb;
14. }
15.
16. //查询一个aabb重叠代理,每个重叠提供AABB的代理都将回调回调类
17. template < span>typename T<
18. inlinevoid b2DynamicTree::Query(T* callback, const b2AABB& aabb) const
19. {
20. //申请临时栈,根节点进栈
21. b2GrowableStack stack;
22. stack.Push(m_root);
23. //判断栈的个数
24. while (stack.GetCount() < 0)
25. {
26. //获取节点id
27. int32 nodeId = stack.Pop();
28. if (nodeId == b2_nullNode)
29. {
30. //节点内存池中的空闲节点
31. continue;
32. }
33. //获取节点
34. const b2TreeNode* node = m_nodes + nodeId;
35. //测试重叠
36. if (b2TestOverlap(node-<aabb, aabb))
37. {
38. //是否是叶子节点
39. if (node-<IsLeaf())
40. {
41. //是否成功
42. bool proceed = callback-<QueryCallback(nodeId);
43. if (proceed == false)
44. {
45. return;
46. }
47. }
48. else
49. {
50. //左右孩子节点进栈
51. stack.Push(node-<child1);
52. stack.Push(node-<child2);
53. }
54. }
55. }
56. }
57. //光线投射
58. template < span>typename T<
59. inlinevoid b2DynamicTree::RayCast(T* callback, const b2RayCastInput& input) const
60. {
61. //
62. b2Vec2 p1 = input.p1;
63. b2Vec2 p2 = input.p2;
64. b2Vec2 r = p2 – p1;
65. b2Assert(r.LengthSquared() < 0.0f);
66. r.Normalize();
67. //v垂直于向量r
68. b2Vec2 v = b2Cross(1.0f, r);
69. b2Vec2 abs_v = b2Abs(v);
70. //分离轴 下面的书籍 p80
71. // 《Collision Detection in Interactive 3D Environments》 by Gino van den Bergen
72. // 【从 http://download.csdn.net/detail/cg0206/4875309 下载】
73. // 计算公式:|dot(v, p1 – c)| < dot(|v|, h)
74. float32 maxFraction = input.maxFraction;
75. // 构建一个绑定的盒子用于该线段
76. b2AABB segmentAABB;
77. {
78. b2Vec2 t = p1 + maxFraction * (p2 – p1);
79. segmentAABB.lowerBound = b2Min(p1, t);
80. segmentAABB.upperBound = b2Max(p1, t);
81. }
82. //创建一个临时栈,并将根节点进栈
83. b2GrowableStack stack;
84. stack.Push(m_root);
85. //栈不为空
86. while (stack.GetCount() < 0)
87. {
88. //出栈
89. int32 nodeId = stack.Pop();
90. if (nodeId == b2_nullNode)
91. {
92. //节点内存池中的空闲节点
93. continue;
94. }
95. //根据节点索引获取节点
96. const b2TreeNode* node = m_nodes + nodeId;
97. //判断AABB
98. if (b2TestOverlap(node-<aabb, segmentAABB) == false)
99. {
100.continue;
101. }
102.
103.//分离轴 p80
104.// |dot(v, p1 – c)| < dot(|v|, h)
105. b2Vec2 c = node-<aabb.GetCenter();
106. b2Vec2 h = node-<aabb.GetExtents();
107.
108. float32 separation = b2Abs(b2Dot(v, p1 – c)) – b2Dot(abs_v, h);
109.if (separation < 0.0f)
110. {
111.continue;
112. }
113.//是否是叶子节点
114.if (node-<IsLeaf())
115. {
116. b2RayCastInput subInput;
117. subInput.p1 = input.p1;
118. subInput.p2 = input.p2;
119. subInput.maxFraction = maxFraction;
120.
121. float32 value = callback-<RayCastCallback(subInput, nodeId);
122.
123.if (value == 0.0f)
124. {
125.//客户端终止了光线投射
126.return;
127. }
128.
129.if (value < 0.0f)
130. {
131.// 更新线段的盒子边界
132. maxFraction = value;
133. b2Vec2 t = p1 + maxFraction * (p2 – p1);
134. segmentAABB.lowerBound = b2Min(p1, t);
135. segmentAABB.upperBound = b2Max(p1, t);
136. }
137. }
138.else
139. {
140.//将两个孩子进栈
141. stack.Push(node-<child1);
142. stack.Push(node-<child2);
143. }
144. }
145.}
获取userData和AABB的,都是根据代理叶子id获取的节点信息。至于Query则是在树上查找每一个与AABB有重叠的叶子节点,并回调,效率比直接使用形状要高很多。
RayCast则用了大量的有关图形碰撞方面的知识和数学方面的计算,《CollisionDetection in Interactive 3D Environments》这本书有提到,本书目前没有找到中文版,对E文有兴趣的朋友可以从这里 下载。偶研究不深,也就不在这里误人子弟了。
我们再看看b2DynamicTree.ccp文件。
1、 构造、析构函数
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:动态树构造函数,初始化数据
3. * 参数说明:(void)
4. * 返 回 值:(void)
5. ***************************************************************************/
6. b2DynamicTree::b2DynamicTree()
7. {
8. m_root = b2_nullNode;
9.
10. m_nodeCapacity = 16;
11. m_nodeCount = 0;
12. //申请一块内存,创建m_nodeCapacity子节点,并清空内存中的内容
13. m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode));
14. memset(m_nodes, 0, m_nodeCapacity * sizeof(b2TreeNode));
15. // 创建一个空闲链表
16. for (int32 i = 0; i < m_nodeCapacity-ispan>
17. {
18. m_nodes[i].next = i + 1;
19. m_nodes[i].height = -1;
20. }
21. //链表的最后一个子节点的孩子指针、高度都置为初始值
22. m_nodes[m_nodeCapacity-1].next = b2_nullNode;
23. m_nodes[m_nodeCapacity-1].height = -1;
24. m_freeList = 0;
25.
26. m_path = 0;
27.
28. m_insertionCount = 0;
29. }
30.
31. /**************************************************************************
32. * 功能描述:析构函数
33. * 参数说明:(void)
34. * 返 回 值:(void)
35. ***************************************************************************/
36. b2DynamicTree::~b2DynamicTree()
37. {
38. //一次性释放整个内存池
39. b2Free(m_nodes);
40. }
关于构造函数b2DynamicTree 主要是对类的成员初始化,并申请一块连续的内存,注意我们这次不是在自己实现的SOA(小型对象分配器)上面申请的,而是直接在内存中用b2Alloc()申请的。此块内存将作为节点内存池供树的节点使用。同时我们还看到有个for循环将所有的节点都遍历了一遍,又是干嘛呢?创建链表呗,看到注释的童鞋也许会这样回答,对的,那创建链表是干什么用的呢?嘿嘿……主要还是查找方便呗。关于析构函数主要是释放整个内存池。
2、对树中节点操作
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:从内存池中申请一个节点.如果必要增大内存池
3. * 参数说明:(void)
4. * 返 回 值:节点指针
5. ***************************************************************************/
6. int32 b2DynamicTree::AllocateNode()
7. {
8. // 如果需要扩大节点内存池
9. if (m_freeList == b2_nullNode)
10. {
11. b2Assert(m_nodeCount == m_nodeCapacity);
12. //空闲链表为空,重新创建一个更大的内存池
13. b2TreeNode* oldNodes = m_nodes;
14. m_nodeCapacity *= 2;
15. m_nodes = (b2TreeNode*)b2Alloc(m_nodeCapacity * sizeof(b2TreeNode));
16. memcpy(m_nodes, oldNodes, m_nodeCount * sizeof(b2TreeNode));
17. b2Free(oldNodes);
18. // 创建一个空闲链表。父节点成为下一个指针
19. // 注意:这次是从m_nodeCount开始的
20. for (int32 i = m_nodeCount; i < m_nodeCapacity-ispan>
21. {
22. m_nodes[i].next = i + 1;
23. m_nodes[i].height = -1;
24. }
25.
26. m_nodes[m_nodeCapacity-1].next = b2_nullNode;
27. m_nodes[m_nodeCapacity-1].height = -1;
28. m_freeList = m_nodeCount;
29. }
30. //从空闲链表中去下一个节点,初始化该节点,
31. //同时将空闲链表头指针m_freeList指向下一个
32. int32 nodeId = m_freeList;
33. m_freeList = m_nodes[nodeId].next;
34. m_nodes[nodeId].parent = b2_nullNode;
35. m_nodes[nodeId].child1 = b2_nullNode;
36. m_nodes[nodeId].child2 = b2_nullNode;
37. m_nodes[nodeId].height = 0;
38. m_nodes[nodeId].userData = NULL;
39. //增加节点数量
40. ++m_nodeCount;
41. //返回节点id
42. return nodeId;
43. }
44.
45.
46. //
47. /**************************************************************************
48. * 功能描述:从内存池中申请一个节点.根据nodeid将一个节点内存返回到节点池内
49. * 参数说明:nodeId:节点指针
50. * 返 回 值:(void)
51. ***************************************************************************/
52. void b2DynamicTree::FreeNode(int32 nodeId)
53. {
54. //验证nodeid的有效性
55. b2Assert(0 < nodeIdnodeIdm_nodeCapacityspan>
56. //验证是否有节点
57. b2Assert(0 < m_nodeCountspan>
58. //将当前节点以头插入的方式放到空闲链表中
59. m_nodes[nodeId].next = m_freeList;
60. m_nodes[nodeId].height = -1;
61. m_freeList = nodeId;
62. //将节点个数减1
63. //注意此处并没有释放
64. –m_nodeCount;
65. }
先看下面的图示:
动态数组m_nodes此时包含两部分,以m_root根的动态树部分用于管理动态树中的用户信息,和以m_freeList为头指针的空闲链表部分,用于管理内存池中未使用的节点。
对于AllocateNode函数,首先从空闲链表中获取其头部指针(索引)并返回,同时调整空闲链表的头指针到下一个节点。注意这里有一种情况,就是当空闲链表没有节点时,我们将重新申请一块是原来2倍内存空间的新的动态数组,并拷贝原来的信息到新的动态数组中,此处我们可以看到无论是m_root还是m_freeList,对其下属保存的都是索引,而非指针。这里拷贝到新的地方仍然有用,而指针则不然,在这里不得不佩服设计者的精妙之处。
对于FreeNode函数,我们将节点置空返回到空闲链表中,也并没有将其真正的释放,这也是很精妙的地方。
3、 对具有aabb代理的节点操作
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:在树上创建一个叶子节点代理
3. * 参数说明:aabb :aabb变量
4. user : 数据
5. * 返 回 值:节点的索引来替代指针,来增长我们的节点池
6. ***************************************************************************/
7. int32 b2DynamicTree::CreateProxy(const b2AABB& aabb, void* userData)
8. {
9. //申请代理节点id
10. int32 proxyId = AllocateNode();
11.
12. // Fatten the aabb.
13. //填充aabb,为节点赋值
14. b2Vec2 r(b2_aabbExtension, b2_aabbExtension);
15. m_nodes[proxyId].aabb.lowerBound = aabb.lowerBound – r;
16. m_nodes[proxyId].aabb.upperBound = aabb.upperBound + r;
17. m_nodes[proxyId].userData = userData;
18. m_nodes[proxyId].height = 0;
19. //插入叶子节点
20. InsertLeaf(proxyId);
21.
22. return proxyId;
23. }
24.
25. /**************************************************************************
26. * 功能描述:销毁叶子代理
27. * 参数说明:proxyId :叶子代理id
28. * 返 回 值:(void)
29. ***************************************************************************/
30. void b2DynamicTree::DestroyProxy(int32 proxyId)
31. {
32. //验证proxyid的有效性
33. b2Assert(0 < proxyIdproxyIdm_nodeCapacityspan>
34. //验证是否是叶子节点
35. b2Assert(m_nodes[proxyId].IsLeaf());
36. //删除叶子节点
37. RemoveLeaf(proxyId);
38. //是否子节点
39. FreeNode(proxyId);
40. }
41. /**************************************************************************
42. * 功能描述:移动叶子代理
43. * 参数说明:proxyId :叶子代理id
44. aabb : aabb变量
45. displacement:移动坐标向量
46. * 返 回 值: (void)
47. ***************************************************************************/
48. bool b2DynamicTree::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement)
49. {
50. //验证proxyid的有效性
51. b2Assert(0 < proxyIdproxyIdm_nodeCapacityspan>
52. //验证是否是叶子节点
53. b2Assert(m_nodes[proxyId].IsLeaf());
54. //检验aabb是否互相包含
55. if (m_nodes[proxyId].aabb.Contains(aabb))
56. {
57. returnfalse;
58. }
59. //根据proxyId移除叶子
60. RemoveLeaf(proxyId);
61. //扩大AABB
62. b2AABB b = aabb;
63. b2Vec2 r(b2_aabbExtension, b2_aabbExtension);
64. b.lowerBound = b.lowerBound – r;
65. b.upperBound = b.upperBound + r;
66. //预测aabb的位移
67. b2Vec2 d = b2_aabbMultiplier * displacement;
68. //扩大下限
69. if (d.x < fspan>
70. {
71. b.lowerBound.x += d.x;
72. }
73. else
74. {
75. b.upperBound.x += d.x;
76. }
77.
78. if (d.y < fspan>
79. {
80. b.lowerBound.y += d.y;
81. }
82. else
83. {
84. b.upperBound.y += d.y;
85. }
86. //重新设置aabb
87. m_nodes[proxyId].aabb = b;
88. //插入叶子节点
89. InsertLeaf(proxyId);
90. returntrue;
91. }
[cpp]view plaincopy
1.
4、对叶子节点进行操作
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:插入叶子节点
3. * 参数说明:leaf :叶子节点指针
4. * 返 回 值: (void)
5. ***************************************************************************/
6. void b2DynamicTree::InsertLeaf(int32 leaf)
7. {
8. //插入节点总数量自增
9. ++m_insertionCount;
10. //判断该树是否为空
11. if (m_root == b2_nullNode)
12. {
13. m_root = leaf;
14. m_nodes[m_root].parent = b2_nullNode;
15. return;
16. }
17. //为该节点找到最好的兄弟(姐妹)节点
18. //获取leaf的aabb
19. b2AABB leafAABB = m_nodes[leaf].aabb;
20. //获取根节点
21. int32 index = m_root;
22. //不是叶子节点
23. while (m_nodes[index].IsLeaf() == false)
24. {
25. int32 child1 = m_nodes[index].child1;
26. int32 child2 = m_nodes[index].child2;
27. //获取aabb的周长
28. float32 area = m_nodes[index].aabb.GetPerimeter();
29. //获取
30. b2AABB combinedAABB;
31. combinedAABB.Combine(m_nodes[index].aabb, leafAABB);
32. float32 combinedArea = combinedAABB.GetPerimeter();
33. //为该节点创建一个新的父节点【一个新叶子节点】
34. float32 cost = 2.0f * combinedArea;
35. // 最小cost推动叶子进一步下降的树的下一层
36. float32 inheritanceCost = 2.0f * (combinedArea – area);
37. //降低cost到child1
38. float32 cost1;
39. //左孩子是叶子节点
40. if (m_nodes[child1].IsLeaf())
41. {
42. //获取cost1
43. b2AABB aabb;
44. aabb.Combine(leafAABB, m_nodes[child1].aabb);
45. cost1 = aabb.GetPerimeter() + inheritanceCost;
46. }
47. else
48. {
49. //获取cost1
50. b2AABB aabb;
51. aabb.Combine(leafAABB, m_nodes[child1].aabb);
52. float32 oldArea = m_nodes[child1].aabb.GetPerimeter();
53. float32 newArea = aabb.GetPerimeter();
54. cost1 = (newArea – oldArea) + inheritanceCost;
55. }
56. //降低cost到child2
57. float32 cost2;
58. if (m_nodes[child2].IsLeaf())
59. {
60. //获取cost2
61. b2AABB aabb;
62. aabb.Combine(leafAABB, m_nodes[child2].aabb);
63. cost2 = aabb.GetPerimeter() + inheritanceCost;
64. }
65. else
66. {
67. //获取cost2
68. b2AABB aabb;
69. aabb.Combine(leafAABB, m_nodes[child2].aabb);
70. float32 oldArea = m_nodes[child2].aabb.GetPerimeter();
71. float32 newArea = aabb.GetPerimeter();
72. cost2 = newArea – oldArea + inheritanceCost;
73. }
74. // 获取最小成本
75. if (cost < costcostcostspan>
76. {
77. break;
78. }
79. //下降到最小cost
80. if (cost1 < costspan>
81. {
82. index = child1;
83. }
84. else
85. {
86. index = child2;
87. }
88. }
89. int32 sibling = index;
90. //创建一个新的父节点
91. //初始化
92. int32 oldParent = m_nodes[sibling].parent;
93. int32 newParent = AllocateNode();
94. m_nodes[newParent].parent = oldParent;
95. m_nodes[newParent].userData = NULL;
96. m_nodes[newParent].aabb.Combine(leafAABB, m_nodes[sibling].aabb);
97. m_nodes[newParent].height = m_nodes[sibling].height + 1;
98.
99. if (oldParent != b2_nullNode)
100. {
101.// 兄弟节点不是根节点
102.if (m_nodes[oldParent].child1 == sibling)
103. {
104. m_nodes[oldParent].child1 = newParent;
105. }
106.else
107. {
108. m_nodes[oldParent].child2 = newParent;
109. }
110.
111. m_nodes[newParent].child1 = sibling;
112. m_nodes[newParent].child2 = leaf;
113. m_nodes[sibling].parent = newParent;
114. m_nodes[leaf].parent = newParent;
115. }
116.else
117. {
118.// 兄弟节点是根节点
119. m_nodes[newParent].child1 = sibling;
120. m_nodes[newParent].child2 = leaf;
121. m_nodes[sibling].parent = newParent;
122. m_nodes[leaf].parent = newParent;
123. m_root = newParent;
124. }
125.// 向后走修复树的高度和aabb
126. index = m_nodes[leaf].parent;
127.while (index != b2_nullNode)
128. {
129.//平衡
130. index = Balance(index);
131.//左右孩子节点
132. int32 child1 = m_nodes[index].child1;
133. int32 child2 = m_nodes[index].child2;
134.
135. b2Assert(child1 != b2_nullNode);
136. b2Assert(child2 != b2_nullNode);
137.//获取高度和aabb
138. m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height);
139. m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb);
140.//获取parent节点
141. index = m_nodes[index].parent;
142. }
143.
144.//Validate();
145.}
146.
147./**************************************************************************
148.* 功能描述:删除叶子节点
149.* 参数说明:leaf :叶子节点指针
150.* 返 回 值: (void)
151.***************************************************************************/
152.void b2DynamicTree::RemoveLeaf(int32 leaf)
153.{
154.//只有一个节点
155.if (leaf == m_root)
156. {
157. m_root = b2_nullNode;
158.return;
159. }
160.//获取父节点和祖父节点
161. int32 parent = m_nodes[leaf].parent;
162. int32 grandParent = m_nodes[parent].parent;
163.//选找兄弟节点
164. int32 sibling;
165.if (m_nodes[parent].child1 == leaf)
166. {
167. sibling = m_nodes[parent].child2;
168. }
169.else
170. {
171. sibling = m_nodes[parent].child1;
172. }
173.// 祖父节点不为空,即父节点不是根节点
174.if (grandParent != b2_nullNode)
175. {
176.// Destroy parent and connect sibling to grandParent.
177.// 销毁父节点和将兄弟节点链接到祖父节点中
178.if (m_nodes[grandParent].child1 == parent)
179. {
180. m_nodes[grandParent].child1 = sibling;
181. }
182.else
183. {
184. m_nodes[grandParent].child2 = sibling;
185. }
186. m_nodes[sibling].parent = grandParent;
187.//释放节点到内存池中
188. FreeNode(parent);
189.// 调整祖先界限
190. int32 index = grandParent;
191.while (index != b2_nullNode)
192. {
193.//平衡
194. index = Balance(index);
195.//获取左右孩子
196. int32 child1 = m_nodes[index].child1;
197. int32 child2 = m_nodes[index].child2;
198.//合并aabb
199.//高度
200. m_nodes[index].aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb);
201. m_nodes[index].height = 1 + b2Max(m_nodes[child1].height, m_nodes[child2].height);
202.//更新index
203. index = m_nodes[index].parent;
204. }
205. }
206.else
207. {
208.//获取根节点
209. m_root = sibling;
210. m_nodes[sibling].parent = b2_nullNode;
211.//释放父节点
212. FreeNode(parent);
213. }
214.
215.//Validate();
216.}
我们首先来说说InsertLeaf函数
它有以下几种情况,如图:
以上就是动态树节点插入的几种情况,主要做法就是:
a)、通过遍历树上的节点,对比aabb找到代价最小的节点A
b)、将A作为兄弟节点,先建一个父节点N,将A的父节点的孩子指针指向N,同时N将A和要插入的叶子节点L作为左右孩子节点连接起来
c)、如果出现树不平衡,旋转动态树,使它成为新的平衡二叉树。大家可以看到,目前上图倒数第一和第二幅图上的都还不是平衡树,这部分等到说的平衡树的函数的时候再弄。
另外关于box2d中的动态树,我们来思考一些问题,就是我们插入的节点会始终是树的叶子节点吗?如果不是的那些节点又到哪去了呢?如果是的怎么做到的呢?
关于这个问题先卖个关子,我们再看看删除叶子函数RemoveLeaf,还是先看图:
关于删除函数,主要步骤是:
a)、根据索引找到父节点、祖父节点、和兄弟节点
b)、将祖父节点的原本指向父节点的孩子指针指向兄弟节点,并释放父亲节点
c)、如果出现树不平衡,旋转动态树,使它成为新的平衡二叉树。
还要注意一点的就是我们插入的有用的信息都是叶子节点,剩下的节点全部都是辅助节点。等我们看到重新构建一棵新的二叉树的时候会对这方面有新的认识。
相信大家对节点操作有一定的认识了吧,现在知道上面问题的答案了吗?其实,不管插入还是删除还是我们下面要说的使树平衡的函数,叶子节点还是叶子节点,丝毫没有变成其他节点,我们插入的有用的信息都是叶子节点,剩下的节点全部都是辅助节点,插入的时候添加父节点,删除的时候同时也删除了父节点。等我们看到重新构建一棵新的二叉树的时候会对这方面有新的认识。
5、 对树的操作
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:如果子树A不平衡,则执行一个向左或向右旋转
3. * 参数说明:iA :子树A
4. * 返 回 值: (void)
5. ***************************************************************************/
6. int32 b2DynamicTree::Balance(int32 iA)
7. {
8. //iA不是根节点
9. b2Assert(iA != b2_nullNode);
10. //获取子树A
11. b2TreeNode* A = m_nodes + iA;
12. //已是平衡树,不需要调整
13. if (A-<IsLeaf() || A-<height < span>
14. {
15. return iA;
16. }
17. // 获取A的左右孩子
18. int32 iB = A-<child1;
19. int32 iC = A-<child2;
20. // iB、iC是否有效
21. b2Assert(0 < iBiBm_nodeCapacityspan>
22. b2Assert(0 < iCiCm_nodeCapacityspan>
23. // 获取子树B、C
24. b2TreeNode* B = m_nodes + iB;
25. b2TreeNode* C = m_nodes + iC;
26. // 获得平衡值
27. int32 balance = C-<height – B-<height;
28.
29. // 上旋C
30. if (balance < 1)
31. {
32. //获取C的左右孩子iF、iG和子树F、G
33. int32 iF = C-<child1;
34. int32 iG = C-<child2;
35. b2TreeNode* F = m_nodes + iF;
36. b2TreeNode* G = m_nodes + iG;
37. // 验证iF、iG是否有效
38. b2Assert(0 < iFiFm_nodeCapacityspan>
39. b2Assert(0 < iGiGm_nodeCapacityspan>
40. // 交换A和C
41. C-<child1 = iA;
42. C-<parent = A-<parent;
43. A-<parent = iC;
44. // A的父指针应该指向c
45. // A不是头节点
46. if (C-<parent != b2_nullNode)
47. {
48. if (m_nodes[C-<parent].child1 == iA)
49. {
50. m_nodes[C-<parent].child1 = iC;
51. }
52. else
53. {
54. b2Assert(m_nodes[C-<parent].child2 == iA);
55. m_nodes[C-<parent].child2 = iC;
56. }
57. }
58. else
59. {
60. //A是头节点
61. m_root = iC;
62. }
63. // 旋转
64. // 如果f的高度大,则旋转F
65. if (F-<height < G-<height)
66. {
67. C-<child2 = iF;
68. A-<child2 = iG;
69. G-<parent = iA;
70. A-<aabb.Combine(B-<aabb, G-<aabb);
71. C-<aabb.Combine(A-<aabb, F-<aabb);
72.
73. A-<height = 1 + b2Max(B-<height, G-<height);
74. C-<height = 1 + b2Max(A-<height, F-<height);
75. }
76. else
77. {
78. // 旋转G
79. C-<child2 = iG;
80. A-<child2 = iF;
81. F-<parent = iA;
82. A-<aabb.Combine(B-<aabb, F-<aabb);
83. C-<aabb.Combine(A-<aabb, G-<aabb);
84.
85. A-<height = 1 + b2Max(B-<height, F-<height);
86. C-<height = 1 + b2Max(A-<height, G-<height);
87. }
88.
89. return iC;
90. }
91.
92. // 上旋B
93. if (balance < -span>
94. {
95. int32 iD = B-<child1;
96. int32 iE = B-<child2;
97. b2TreeNode* D = m_nodes + iD;
98. b2TreeNode* E = m_nodes + iE;
99. b2Assert(0 < iDiDm_nodeCapacityspan>
100. b2Assert(0 < iEiEm_nodeCapacityspan>
101.
102.//交换A和B
103. B-<child1 = iA;
104. B-<parent = A-<parent;
105. A-<parent = iB;
106.// A的旧父指针指向B
107.if (B-<parent != b2_nullNode)
108. {
109.if (m_nodes[B-<parent].child1 == iA)
110. {
111. m_nodes[B-<parent].child1 = iB;
112. }
113.else
114. {
115. b2Assert(m_nodes[B-<parent].child2 == iA);
116. m_nodes[B-<parent].child2 = iB;
117. }
118. }
119.else
120. {
121. m_root = iB;
122. }
123.
124.//旋转
125.if (D-<height < E-<height)
126. {
127.// 旋转D
128. B-<child2 = iD;
129. A-<child1 = iE;
130. E-<parent = iA;
131. A-<aabb.Combine(C-<aabb, E-<aabb);
132. B-<aabb.Combine(A-<aabb, D-<aabb);
133.
134. A-<height = 1 + b2Max(C-<height, E-<height);
135. B-<height = 1 + b2Max(A-<height, D-<height);
136. }
137.else
138. {
139.// 旋转E
140. B-<child2 = iE;
141. A-<child1 = iD;
142. D-<parent = iA;
143. A-<aabb.Combine(C-<aabb, D-<aabb);
144. B-<aabb.Combine(A-<aabb, E-<aabb);
145.
146. A-<height = 1 + b2Max(C-<height, D-<height);
147. B-<height = 1 + b2Max(A-<height, E-<height);
148. }
149.
150.return iB;
151. }
152.
153.return iA;
154.}
155./**************************************************************************
156.* 功能描述:获得树的高度
157.* 参数说明:(void)
158.* 返 回 值:树的高度
159.***************************************************************************/
160.int32 b2DynamicTree::GetHeight() const
161.{
162.if (m_root == b2_nullNode)
163. {
164.return 0;
165. }
166.
167.return m_nodes[m_root].height;
168.}
169.
170./**************************************************************************
171.* 功能描述:获得节点总数“面积”之和根“面积”的比
172.* 参数说明:(void)
173.* 返 回 值:树的高度
174.***************************************************************************/
175.float32 b2DynamicTree::GetAreaRatio() const
176.{
177.//空树
178.if (m_root == b2_nullNode)
179. {
180.return 0.0f;
181. }
182.//获取根子树
183.const b2TreeNode* root = m_nodes + m_root;
184. float32 rootArea = root-<aabb.GetPerimeter();
185.//获取所有节点的总“面积”,其实是周长
186. float32 totalArea = 0.0f;
187.for (int32 i = 0; i < m_nodeCapacityispan>
188. {
189.const b2TreeNode* node = m_nodes + i;
190.if (node-<height < span>
191. {
192.//内存池内的空闲节点
193.continue;
194. }
195.
196. totalArea += node-<aabb.GetPerimeter();
197. }
198.//获取比率
199.return totalArea / rootArea;
200.}
201./**************************************************************************
202.* 功能描述:计算子树的高度
203.* 参数说明:nodeid:子树的头指针索引
204.* 返 回 值:子树的高度
205.***************************************************************************/
206.int32 b2DynamicTree::ComputeHeight(int32 nodeId) const
207.{
208.//验证子树的头指针索引
209. b2Assert(0 < nodeIdnodeIdm_nodeCapacityspan>
210.//获取子树头节点
211. b2TreeNode* node = m_nodes + nodeId;
212.// 是否是叶子
213.if (node-<IsLeaf())
214. {
215.return 0;
216. }
217.//递给调用,返回高度
218. int32 height1 = ComputeHeight(node-<child1);
219. int32 height2 = ComputeHeight(node-<child2);
220.return 1 + b2Max(height1, height2);
221.}/**************************************************************************
222.* 功能描述:计算树的高度
223.* 参数说明:(void)
224.* 返 回 值:树的高度
225.***************************************************************************/
226.int32 b2DynamicTree::ComputeHeight() const
227.{
228. int32 height = ComputeHeight(m_root);
229.return height;
230.}
231./**************************************************************************
232.* 功能描述:验证子树的结构
233.* 参数说明:index:子树的索引值
234.* 返 回 值:(void)
235.***************************************************************************/
236.void b2DynamicTree::ValidateStructure(int32 index) const
237.{
238.//空树
239.if (index == b2_nullNode)
240. {
241.return;
242. }
243.//子树是整个树
244.if (index == m_root)
245. {
246.//验证有效性
247. b2Assert(m_nodes[index].parent == b2_nullNode);
248. }
249.//获取子树的头指针
250.const b2TreeNode* node = m_nodes + index;
251.//获取左右孩子
252. int32 child1 = node-<child1;
253. int32 child2 = node-<child2;
254.//node是否是叶子节点
255.if (node-<IsLeaf())
256. {
257.//验证左右孩子和高度
258. b2Assert(child1 == b2_nullNode);
259. b2Assert(child2 == b2_nullNode);
260. b2Assert(node-<height == 0);
261.return;
262. }
263.//验证左右孩子
264. b2Assert(0 < childchildm_nodeCapacityspan>
265. b2Assert(0 < childchildm_nodeCapacityspan>
266.//验证左右孩子的父节点
267. b2Assert(m_nodes[child1].parent == index);
268. b2Assert(m_nodes[child2].parent == index);
269.//递归验证左右孩子的结构
270. ValidateStructure(child1);
271. ValidateStructure(child2);
272.}
273./**************************************************************************
274.* 功能描述:验证子树的度量值
275.* 参数说明:index:子树的索引值
276.* 返 回 值:(void)
277.***************************************************************************/
278.void b2DynamicTree::ValidateMetrics(int32 index) const
279.{
280.//空子树
281.if (index == b2_nullNode)
282. {
283.return;
284. }
285.//子树的头指针
286.const b2TreeNode* node = m_nodes + index;
287.//获取左右孩子
288. int32 child1 = node-<child1;
289. int32 child2 = node-<child2;
290.//子树是否是叶子节点
291.if (node-<IsLeaf())
292. {
293.//验证左右孩子和高度
294. b2Assert(child1 == b2_nullNode);
295. b2Assert(child2 == b2_nullNode);
296. b2Assert(node-<height == 0);
297.return;
298. }
299.//验证左右孩子
300. b2Assert(0 < childchildm_nodeCapacityspan>
301. b2Assert(0 < childchildm_nodeCapacityspan>
302.//验证高度
303. int32 height1 = m_nodes[child1].height;
304. int32 height2 = m_nodes[child2].height;
305. int32 height;
306. height = 1 + b2Max(height1, height2);
307. b2Assert(node-<height == height);
308.//验证aabb
309. b2AABB aabb;
310. aabb.Combine(m_nodes[child1].aabb, m_nodes[child2].aabb);
311.
312. b2Assert(aabb.lowerBound == node-<aabb.lowerBound);
313. b2Assert(aabb.upperBound == node-<aabb.upperBound);
314.//递归验证左右孩子子树
315. ValidateMetrics(child1);
316. ValidateMetrics(child2);
317.}
318.
319./**************************************************************************
320.* 功能描述:验证这棵树,用于测试
321.* 参数说明:(void)
322.* 返 回 值:(void)
323.***************************************************************************/
324.void b2DynamicTree::Validate() const
325.{
326.//验证这棵树的结构和度量
327. ValidateStructure(m_root);
328. ValidateMetrics(m_root);
329.//遍历空闲链表,计算空闲节点的个数
330. int32 freeCount = 0;
331. int32 freeIndex = m_freeList;
332.while (freeIndex != b2_nullNode)
333. {
334. b2Assert(0 < freeIndexfreeIndexm_nodeCapacityspan>
335. freeIndex = m_nodes[freeIndex].next;
336. ++freeCount;
337. }
338.//验证高度
339. b2Assert(GetHeight() == ComputeHeight());
340.//验证内存池中的节点总容量
341. b2Assert(m_nodeCount + freeCount == m_nodeCapacity);
342.}
343./**************************************************************************
344.* 功能描述:获取平衡值
345.* 参数说明:获取最大的平衡值
346.* 返 回 值:(void)
347.***************************************************************************/
348.int32 b2DynamicTree::GetMaxBalance() const
349.{
350.//
351. int32 maxBalance = 0;
352.for (int32 i = 0; i < m_nodeCapacityispan>
353. {
354.const b2TreeNode* node = m_nodes + i;
355.// 内存池中的空闲节点
356.if (node-<height < span>
357. {
358.continue;
359. }
360.
361. b2Assert(node-<IsLeaf() == false);
362.//获取最大平衡值
363. int32 child1 = node-<child1;
364. int32 child2 = node-<child2;
365. int32 balance = b2Abs(m_nodes[child2].height – m_nodes[child1].height);
366. maxBalance = b2Max(maxBalance, balance);
367. }
368.
369.return maxBalance;
370.}
371./**************************************************************************
372.* 功能描述:构建一个最优的树,非常昂贵,用于测试
373.* 参数说明:(void)
374.* 返 回 值:(void)
375.***************************************************************************/
376.void b2DynamicTree::RebuildBottomUp()
377.{
378.//从系统堆中申请一段内存
379. int32* nodes = (int32*)b2Alloc(m_nodeCount * sizeof(int32));
380. int32 count = 0;
381.//创建空闲的叶子数组。其余是空闲的
382.for (int32 i = 0; i < m_nodeCapacityispan>
383. {
384.if (m_nodes[i].height < span>
385. {
386.// 内存池中空闲的节点
387.continue;
388. }
389.// 是否是叶子节点
390.if (m_nodes[i].IsLeaf())
391. {
392.
393. m_nodes[i].parent = b2_nullNode;
394. nodes[count] = i;
395. ++count;
396. }
397.else
398. {
399.// 不是则释放到内存池中
400. FreeNode(i);
401. }
402. }
403.// 叶子节点的个数
404.while (count < 1)
405. {
406.//最小cost
407. float32 minCost = b2_maxFloat;
408. int32 iMin = -1, jMin = -1;
409.//获取最小(j)和第二小(i)的cost
410.for (int32 i = 0; i < countispan>
411. {
412. b2AABB aabbi = m_nodes[nodes[i]].aabb;
413.for (int32 j = i + 1; j < countjspan>
414. {
415. b2AABB aabbj = m_nodes[nodes[j]].aabb;
416. b2AABB b;
417. b.Combine(aabbi, aabbj);
418. float32 cost = b.GetPerimeter();
419.//获取最小的cost
420.if (cost < minCostspan>
421. {
422. iMin = i;
423. jMin = j;
424. minCost = cost;
425. }
426. }
427. }
428.//获取左右孩子节点和左右子树
429. int32 index1 = nodes[iMin];
430. int32 index2 = nodes[jMin];
431. b2TreeNode* child1 = m_nodes + index1;
432. b2TreeNode* child2 = m_nodes + index2;
433.//申请父子树索引
434. int32 parentIndex = AllocateNode();
435.//获取父子树节点
436. b2TreeNode* parent = m_nodes + parentIndex;
437. parent-<child1 = index1;
438. parent-<child2 = index2;
439. parent-<height = 1 + b2Max(child1-<height, child2-<height);
440. parent-<aabb.Combine(child1-<aabb, child2-<aabb);
441. parent-<parent = b2_nullNode;
442.//
443. child1-<parent = parentIndex;
444. child2-<parent = parentIndex;
445.//覆盖最小aabb节点
446. nodes[jMin] = nodes[count-1];
447.//将第二小aabb节点用父节点覆盖
448. nodes[iMin] = parentIndex;
449. –count;
450. }
451.//获取跟节点
452. m_root = nodes[0];
453.//释放节点
454. b2Free(nodes);
455.//验证这棵树,用于测试
456. Validate();
457.}
这部分,我们主要来看一看Blance函数和RebuildBottomUp函数,Blance函数主要是调节树的平衡,使之成为平衡二叉树。主要步骤是:
a)、获取当前节点的两个孩子节点,比对孩子子树B、C的高度,求的高度差b
b)、若b不在[-1,1]之间,则上旋孩子子树较高的(这里假设是B),将父节点作为子树B的一个孩子,同时树的根节点指针m_root指向B节点。
c)、然后在B子树上进行a、b操作,直到叶子节点。同时返回新的根节点
我们就用上图中插入时形成的动态树但还没有进行平衡处理的树进行调整,如图:
哈哈,一棵平衡树就这样就成了,当然这个步骤不复杂,但是原理是一样的,就不介绍了。注意,我们可以看到,平衡操作后,叶子节点依然是叶子节点,我们所需要的信息还是在那里面。
下面我们说说RebuildBottomUp这个函数,它主要重新构建一棵树。
主要步骤是:
a)、获取所有叶子节点放入动态数组中,其他释放到叶子节点内存池中
b)、获取所有叶子节点中aabb最小的两个,申请一个节点,作为它们的父节点,形成一个新的子树
c)、用动态数组最后一个节点覆盖最小aabb的节点,用父节点放入动态数组aabb第二小的位置上。同时将动态数组中的节点个数减少一个。
d)、重复a、b、c步骤,直到动态数组中的节点个数为1个。
e)、获取头指针。
今天的动态树部分就说到这了,如有本人理解不妥或错误之处,望大家多多指正,同时也希望和大家多多交流。
在一个物理步长内,碰撞处理可以被划分成narrow-phase和broad-phase两个阶段。在narrow-phase阶段计算一对形状的接触。假设有N个形状,直接使用蛮力进行计算,我们需要调用N*N/2次narrow-phase算法。
b2BroadPhase类通过使用动态树降低了管理数据方面的开销。这极大的降低了调用narrow-phase算法的次数。
一般情况下,你不需要直接和broad-phase打交道。Box2D来内部来创建和管理broad-phase。另外,b2BroadPhase是使用Box2D的模拟循环的思路来设计的,所以它可能不适合用于其他用途。
—摘自oh!coder的博客
Box2d中broad-phase用于计算pairs【相交记录】,执行容量查询和光线投射。主要还是调用上一节我们说的动态树进行数据方面的管理。首先,我们还是看看头文件b2BroadPhase.h中的定义部分。
[cpp]view plaincopy
1. //pair定义
2. struct b2Pair
3. {
4. int32 proxyIdA; //代理a
5. int32 proxyIdB; //代理b
6. int32 next; //下一个pair
7. };
8.
9. // broad-phase用于计算pairs,执行体积查询和光线投射
10. // broad-phase不会持续pairs.相反,它会汇报新的pairs。这取决于客户端是否用掉新的pairs和是否跟踪后续重叠。
11. class b2BroadPhase
12. {
13. public:
14. //空节点代理
15. enum
16. {
17. e_nullProxy = -1
18. };
19.
20. b2BroadPhase();
21. ~b2BroadPhase();
22. /**************************************************************************
23. * 功能描述:创建一个代理,并用aabb初始化。pairs不会汇报直到UpdatePairs被调用
24. * 参数说明: allocator :soa分配器对象指针
25. userData :用户数据
26. * 返 回 值: (void)
27. ***************************************************************************/
28. int32 CreateProxy(const b2AABB& aabb, void* userData);
29. /**************************************************************************
30. * 功能描述:销毁一个代理,任何pairs的删除都取决于客户端
31. * 参数说明: proxyId :代理id
32. * 返 回 值: (void)
33. ***************************************************************************/
34. void DestroyProxy(int32 proxyId);
35.
36. /**************************************************************************
37. * 功能描述:移动一个代理。只要你喜欢可以多次调用MoveProxy,
38. 当你完成后调用UpdatePairs用于完成代理pairs(在你的时间步内)
39. * 参数说明: proxyId :代理id
40. aabb :aabb变量
41. displacement :移动坐标向量
42. * 返 回 值: (void)
43. ***************************************************************************/
44. void MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement);
45.
46. /**************************************************************************
47. * 功能描述: 在下次调用UpdatePairs时,调用一个触发器触发它的pairs
48. * 参数说明: proxyId :代理id
49. * 返 回 值: (void)
50. ***************************************************************************/
51. void TouchProxy(int32 proxyId);
52. /**************************************************************************
53. * 功能描述: 获取宽大的aabb
54. * 参数说明: proxyId :代理id
55. * 返 回 值: (void)
56. ***************************************************************************/
57. const b2AABB& GetFatAABB(int32 proxyId) const;
58. /**************************************************************************
59. * 功能描述: 通过一个代理获取userData,如果id无效,返回NULL
60. * 参数说明: proxyId :代理id
61. * 返 回 值: 用户数据
62. ***************************************************************************/
63. void* GetUserData(int32 proxyId) const;
64. /**************************************************************************
65. * 功能描述: 测试宽大aabb的重复部分
66. * 参数说明: proxyIdA :A代理id
67. proxyIdB :B代理id
68. * 返 回 值: true :不重叠
69. false:重 叠
70. ***************************************************************************/
71. bool TestOverlap(int32 proxyIdA, int32 proxyIdB) const;
72. /**************************************************************************
73. * 功能描述: 获取代理数量
74. * 参数说明: (void)
75. * 返 回 值: 代理数量
76. ***************************************************************************/
77. int32 GetProxyCount() const;
78. /**************************************************************************
79. * 功能描述: 更新pairs.这会对pair进行回调。只能添加pairs
80. * 参数说明: callback :回调对象
81. * 返 回 值: (void)
82. ***************************************************************************/
83. template < span>typename T<
84. void UpdatePairs(T* callback);
85. /**************************************************************************
86. * 功能描述: 在重叠代理中查询一个aabb.每个提供aabb重叠的代理将会被回调类调用
87. * 参数说明: callback :回调对象类
88. aabb :aabb变量
89. * 返 回 值: (void)
90. ***************************************************************************/
91. template < span>typename T<
92. void Query(T* callback, const b2AABB& aabb) const;
93. /**************************************************************************
94. * 功能描述: 光线投射在树上的代理。
95. 这依赖于回调被执行一个精确的光线投射在一个代理包含一个形状
96. * 参数说明: callback : 一个回调对象类,当被调用时,光线将会撒到每个代理中。
97. input :光线投射输入数据。这个光线从p1扩展到p1+maxFraction *(p2 – p1)
98. * 返 回 值: (void)
99. ***************************************************************************/
100.template < span>typename T<
101.void RayCast(T* callback, const b2RayCastInput& input) const;
102./**************************************************************************
103. * 功能描述: 获取嵌入树的高度
104. * 参数说明: (void)
105. * 返 回 值: (void)
106. ***************************************************************************/
107. int32 GetTreeHeight() const;
108./**************************************************************************
109. * 功能描述: 获取嵌入树的平衡值
110. * 参数说明: (void)
111. * 返 回 值: (void)
112. ***************************************************************************/
113. int32 GetTreeBalance() const;
114./**************************************************************************
115. * 功能描述: 获取嵌入树的质量,即是树的总aabbs周长与根节点aabb周长的比
116. * 参数说明: (void)
117. * 返 回 值: 树的质量
118. ***************************************************************************/
119. float32 GetTreeQuality() const;
120.
121.private:
122.//友元类
123.friendclass b2DynamicTree;
124./**************************************************************************
125. * 功能描述: 根据代理id添加代理到移动缓冲区中
126. * 参数说明: proxyId :代理id
127. * 返 回 值: (void)
128. ***************************************************************************/
129.void BufferMove(int32 proxyId);
130./**************************************************************************
131. * 功能描述: 将代理移出移动缓存区
132. * 参数说明: proxyId :代理id
133. * 返 回 值: (void)
134. ***************************************************************************/
135.void UnBufferMove(int32 proxyId);
136./**************************************************************************
137. * 功能描述: 查询回调函数
138. * 参数说明: proxyId :代理id
139. * 返 回 值: true :表示正常回调
140. ***************************************************************************/
141.bool QueryCallback(int32 proxyId);
142.//动态树声明
143. b2DynamicTree m_tree;
144.//代理数量
145. int32 m_proxyCount;
146.//移动的缓冲区
147. int32* m_moveBuffer;
148.//移动缓冲区的总容量
149. int32 m_moveCapacity;
150.//需要移动的代理数量
151. int32 m_moveCount;
152.//pair缓冲区
153. b2Pair* m_pairBuffer;
154.//pair缓冲区中的总容量
155. int32 m_pairCapacity;
156.//pair数量
157. int32 m_pairCount;
158.//查询代理id
159. int32 m_queryProxyId;
160.};
在这类中,可以看到b2BroadPhase将b2DynamicTree定义为友元类,也就是说b2DynamicTree每一个对象均可访问b2BroadPhase的任何成员,不管是否是私有的。同时我们又可以看到在b2BrodPhase类中,我们定义了个动态树对象m_tree,这样我们形成的好像是形成了一个环,但m_tree并不能访问b2DynamicTree中的私有变量。其他部分,不多说了,看注释。再来看内联函数的实现。
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述: 用于pairs的排序
3. * 参数说明: pair1:Pari对象引用
4. pair2: Pari对象引用
5. * 返 回 值: true : pair1较小
6. fasle:pair2较小
7. ***************************************************************************/
8. inlinebool b2PairLessThan(const b2Pair& pair1, const b2Pair& pair2)
9. {
10. //比对pair的代理idA
11. if (pair1.proxyIdA < pairproxyIdAspan>
12. {
13. returntrue;
14. }
15. //再比对代理idB
16. if (pair1.proxyIdA == pair2.proxyIdA)
17. {
18. return pair1.proxyIdB < pairproxyIdBspan>
19. }
20.
21. returnfalse;
22. }
23.
24.
25. //根据代理id获取userData
26. inlinevoid* b2BroadPhase::GetUserData(int32 proxyId) const
27. {
28. return m_tree.GetUserData(proxyId);
29. }
30. //测试重叠
31. inlinebool b2BroadPhase::TestOverlap(int32 proxyIdA, int32 proxyIdB) const
32. {
33. const b2AABB& aabbA = m_tree.GetFatAABB(proxyIdA);
34. const b2AABB& aabbB = m_tree.GetFatAABB(proxyIdB);
35. return b2TestOverlap(aabbA, aabbB);
36. }
37. //获取aabb
38. inlineconst b2AABB& b2BroadPhase::GetFatAABB(int32 proxyId) const
39. {
40. return m_tree.GetFatAABB(proxyId);
41. }
42. //获取代理数量
43. inline int32 b2BroadPhase::GetProxyCount() const
44. {
45. return m_proxyCount;
46. }
47. //获取树的高度
48. inline int32 b2BroadPhase::GetTreeHeight() const
49. {
50. return m_tree.GetHeight();
51. }
52. //获取树的平衡值
53. inline int32 b2BroadPhase::GetTreeBalance() const
54. {
55. return m_tree.GetMaxBalance();
56. }
57. //获取树的质量
58. inline float32 b2BroadPhase::GetTreeQuality() const
59. {
60. return m_tree.GetAreaRatio();
61. }
62. //更新pairs
63. template < span>typename T<
64. void b2BroadPhase::UpdatePairs(T* callback)
65. {
66. //重置pair缓存区
67. m_pairCount = 0;
68. //执行查询树上所有需要移动代理
69. for (int32 i = 0; i < m_moveCountispan>
70. {
71. m_queryProxyId = m_moveBuffer[i];
72. if (m_queryProxyId == e_nullProxy)
73. {
74. continue;
75. }
76. // 我们需要查询树的宽大的AABB,以便当我们创建pair失败时,可以再次创建
77. const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId);
78. // 查询树,创建多个pair并将他们添加到pair缓冲区中
79. m_tree.Query(this, fatAABB);
80. }
81. //重置移动缓冲区
82. m_moveCount = 0;
83. // 排序pair缓冲区
84. std::sort(m_pairBuffer, m_pairBuffer + m_pairCount, b2PairLessThan);
85. // 发送pair到客户端
86. int32 i = 0;
87. while (i < m_pairCountspan>
88. {
89. //在pair缓冲区中获取当前的pair
90. b2Pair* primaryPair = m_pairBuffer + i;
91. //根据相交记录
92. void* userDataA = m_tree.GetUserData(primaryPair-<proxyIdA);
93. void* userDataB = m_tree.GetUserData(primaryPair-<proxyIdB);
94.
95. callback-<AddPair(userDataA, userDataB);
96. ++i;
97.
98. //跳过重复的pair
99. while (i < m_pairCountspan>
100. {
101. b2Pair* pair = m_pairBuffer + i;
102.if (pair-<proxyIdA != primaryPair-<proxyIdA || pair-<proxyIdB != primaryPair-<proxyIdB)
103. {
104.break;
105. }
106. ++i;
107. }
108. }
109.
110.// Try to keep the tree balanced.
111.//m_tree.Rebalance(4);
112.}
113.//区域查询
114.template < span>typename T<
115.inlinevoid b2BroadPhase::Query(T* callback, const b2AABB& aabb) const
116.{
117. m_tree.Query(callback, aabb);
118.}
119.//光线投射
120.template < span>typename T<
121.inlinevoid b2BroadPhase::RayCast(T* callback, const b2RayCastInput& input) const
122.{
123. m_tree.RayCast(callback, input);
124.}
关于这部分,就是对动态树中相关方法的封装,如果还有童鞋有疑问的话,不妨看看我的上一篇文章《Box2d源码学习< span>六<动态树的实现》,接下来来看看b2BroadPhase部分。
[cpp]view plaincopy
1. //构造函数,初始化数据
2. b2BroadPhase::b2BroadPhase()
3. {
4. m_proxyCount = 0;
5.
6. m_pairCapacity = 16;
7. m_pairCount = 0;
8. m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));
9.
10. m_moveCapacity = 16;
11. m_moveCount = 0;
12. m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));
13. }
14. //析构函数
15. b2BroadPhase::~b2BroadPhase()
16. {
17. b2Free(m_moveBuffer);
18. b2Free(m_pairBuffer);
19. }
20. //创建一个代理
21. int32 b2BroadPhase::CreateProxy(const b2AABB& aabb, void* userData)
22. {
23. //获取代理id
24. int32 proxyId = m_tree.CreateProxy(aabb, userData);
25. //代理数量自增
26. ++m_proxyCount;
27. //添加代理到移动缓冲区中
28. BufferMove(proxyId);
29. return proxyId;
30. }
31. //销毁一个代理
32. void b2BroadPhase::DestroyProxy(int32 proxyId)
33. {
34. UnBufferMove(proxyId);
35. –m_proxyCount;
36. m_tree.DestroyProxy(proxyId);
37. }
38. //移动一个代理
39. void b2BroadPhase::MoveProxy(int32 proxyId, const b2AABB& aabb, const b2Vec2& displacement)
40. {
41. bool buffer = m_tree.MoveProxy(proxyId, aabb, displacement);
42. if (buffer)
43. {
44. BufferMove(proxyId);
45. }
46. }
47. //在下次调用UpdatePairs时,调用一个触发器触发它的pairs
48. void b2BroadPhase::TouchProxy(int32 proxyId)
49. {
50. BufferMove(proxyId);
51. }
52. //根据代理id添加代理到移动缓冲区中
53. void b2BroadPhase::BufferMove(int32 proxyId)
54. {
55. //移动缓冲区过小,增容
56. if (m_moveCount == m_moveCapacity)
57. {
58. //获取移动缓冲区
59. int32* oldBuffer = m_moveBuffer;
60. //将容量扩增为原来的2倍
61. m_moveCapacity *= 2;
62. //重新申请移动缓冲区
63. m_moveBuffer = (int32*)b2Alloc(m_moveCapacity * sizeof(int32));
64. //拷贝旧的移动缓冲区内容到新的里面去,并释放旧的移动缓冲区
65. memcpy(m_moveBuffer, oldBuffer, m_moveCount * sizeof(int32));
66. b2Free(oldBuffer);
67. }
68. //添加代理id到移动缓冲区中
69. m_moveBuffer[m_moveCount] = proxyId;
70. //自增
71. ++m_moveCount;
72. }
73. //移除移动缓存区
74. void b2BroadPhase::UnBufferMove(int32 proxyId)
75. {
76. //查找相应的代理
77. for (int32 i = 0; i < m_moveCountispan>
78. {
79. //找到代理,并置空
80. if (m_moveBuffer[i] == proxyId)
81. {
82. m_moveBuffer[i] = e_nullProxy;
83. return;
84. }
85. }
86. }
87. //当我们聚集pairs时这个函数将会被b2DynamicTree:Query调用
88. bool b2BroadPhase::QueryCallback(int32 proxyId)
89. {
90. // 一个代理不需要自己pair更新自己的pair
91. if (proxyId == m_queryProxyId)
92. {
93. returntrue;
94. }
95. // 如果需要增加pair缓冲区
96. if (m_pairCount == m_pairCapacity)
97. {
98. //获取旧的pair缓冲区,并增加容量
99. b2Pair* oldBuffer = m_pairBuffer;
100. m_pairCapacity *= 2;
101.//重新申请pair缓冲区,并拷贝旧缓冲区中的内容
102. m_pairBuffer = (b2Pair*)b2Alloc(m_pairCapacity * sizeof(b2Pair));
103. memcpy(m_pairBuffer, oldBuffer, m_pairCount * sizeof(b2Pair));
104.//释放旧的pair缓冲区
105. b2Free(oldBuffer);
106. }
107.//设置最新的pair
108.//并自增pair数量
109. m_pairBuffer[m_pairCount].proxyIdA = b2Min(proxyId, m_queryProxyId);
110. m_pairBuffer[m_pairCount].proxyIdB = b2Max(proxyId, m_queryProxyId);
111. ++m_pairCount;
112.
113.returntrue;
114.}
通过源代码我们可以看到,它的实现主要靠移动缓冲区(m_moveBuffer)和pair缓冲区(m_pariBuffer)。构造函数b2BroadPhase()主要是申请这两个缓冲区,析构函数~b2BroadPhase()释放这两个缓冲区,创建一个代理函数CreateProxy()主要添加代理到移动缓冲区,销毁代理函数DestroyProxy主要是销毁一个在移动缓冲区的代理,MoveProxy()、TouchProxy()、BufferMove()均是在移动缓冲区中添加代理,UnBufferMove()是在移动缓冲区中移除代理,QueryCallback()是对pair缓冲区的操作。
突然我们心中有一个疑问,这两个缓冲区各自操作各自的,通过这段代码我们看不到任何的联系,它们到底是如何通信的呢?请先大家思考下。。。
好了,大家知道了吗?有人猜到是通过UpdatePairs函数实现的,这是正确的,但具体的还是通过动态树中的Query函数来实现的,我们不妨回顾一下updatepairs中的代码段。
[cpp]view plaincopy
1. //执行查询树上所有需要移动代理
2. for (int32 i = 0; i < m_moveCountispan>
3. {
4. m_queryProxyId = m_moveBuffer[i];
5. if (m_queryProxyId == e_nullProxy)
6. {
7. continue;
8. }
9. // 我们需要查询树的宽大的AABB,以便当我们创建pair失败时,可以再次创建
10. const b2AABB& fatAABB = m_tree.GetFatAABB(m_queryProxyId);
11. // 查询树,创建多个pair并将他们添加到pair缓冲区中
12. m_tree.Query(this, fatAABB);
13. }
有人也许会说,这段代码我们只看到移动缓冲区(m_moveBuffer),看不出来与pair缓冲区(m_pariBuffer)有任何关系,它们怎么产生联系的呢?注意m_tree.Query(this,fatAABB)这句代码和它传递的参数,this表示当前类的对象,fatAABB表示移动缓冲区中一个代理的AABB,存储了代理的信息。我们再回顾一下query函数:
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:查询一个aabb重叠代理,每个重叠提供AABB的代理都将回调回调类
3. * 参数说明:callback :回调对象
4. aabb :要查询的aabb
5. * 返 回 值:aabb对象
6. ***************************************************************************/
7. template < span>typename T<
8. void Query(T* callback, const b2AABB& aabb) const;
再看Query函数中的代码片段
[cpp]view plaincopy
1. //是否成功
2. bool proceed = callback-<QueryCallback(nodeId);
3. if (proceed == false)
4. {
5. return;
6. }
看这句代码bool proceed = callback-<QueryCallback(nodeId); 此处的callback就是刚刚传进去的this,也就是说我们调用的QueryCallback也就是b2BroadPhase::QueryCallback(int32 proxyId)函数。到此处相信大家已经明白了。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多之出。同时也希望能与大家多多交流,共同进步。
Box2d中每种物体它都是要有形状的,关于形状的是如何定义和实现的,我们今天就来说说。要将一个形状描述出来,我们必须知道它的定义,不禁要问形状是什么呢?用来干什么的呢?形状是几何体,用来给物体(body)定型的。同时形状可以在物理模拟中独立使用,你就可以对形状进行各种操作。
父形状的实现只有一个b2Shape.h文件,我们就来看下它是怎么实现的。
[cpp]view plaincopy
1. // 这个拥有质量,用于形状的计算
2. struct b2MassData
3. {
4. // 形状的质量,通常单位是kg
5. float32 mass;
6. //形状的质心到形状的原点
7. b2Vec2 center;
8. //形状的转动惯量
9. float32 I;
10. };
11.
12. //一个形状用于碰撞检测。只要你喜欢,你可以创建一个形状。
13. // 在b2World世界中,形状用于模拟,当创建一个b2Fixture时将被自动创建。
14. //形状类将被封装成一个或多个子形状类
15. class b2Shape
16. {
17. public:
18. //形状类型
19. enum Type
20. {
21. e_circle = 0, //圆形
22. e_edge = 1, //边缘形状
23. e_polygon = 2, //多边形
24. e_chain = 3, //链形状
25. e_typeCount = 4 //形状总数
26. };
27.
28. virtual ~b2Shape() {}
29. /**************************************************************************
30. * 功能描述:用soa块分配器克隆一个具体的形状
31. * 参数说明: allocator :soa分配器对象指针
32. * 返 回 值: 形状指针
33. ***************************************************************************/
34. virtual b2Shape* Clone(b2BlockAllocator* allocator) const = 0;
35. /**************************************************************************
36. * 功能描述:获取孩子形状类型,你可以使用它去创建形状
37. * 参数说明: (void)
38. * 返 回 值: 孩子形状个数
39. ***************************************************************************/
40. Type GetType() const;
41. /**************************************************************************
42. * 功能描述:获取形状的孩子元素的数量,
43. 主要用于chainShape中,到时我们再谈
44. * 参数说明: (void)
45. * 返 回 值: 孩子元素的数量
46. ***************************************************************************/
47. virtual int32 GetChildCount() const = 0;
48. /**************************************************************************
49. * 功能描述:在这个形状中测试这个点的密封性,只适合用于凸的形状
50. * 参数说明: xf : 形状的变换
51. p : world坐标中的一个点
52. * 返 回 值: true : 密封
53. false:敞开
54. ***************************************************************************/
55. virtualbool TestPoint(const b2Transform& xf, const b2Vec2& p) const = 0;
56. /**************************************************************************
57. * 功能描述:投射一束光到一个孩子形状中
58. * 参数说明: output :输出光线投射的结果
59. input :输入光线投射
60. transform :变换应用到此形状中
61. childeIndex :孩子形状索引
62. * 返 回 值: true : 成功
63. false:失败
64. ***************************************************************************/
65. virtualbool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
66. const b2Transform& transform, int32 childIndex) const = 0;
67. /**************************************************************************
68. * 功能描述:给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
69. * 参数说明: aabb : 孩子形状的aabb指针
70. xf : 一个变换的引用
71. childIndex : 孩子的索引值
72. * 返 回 值: (void)
73. ***************************************************************************/
74. virtualvoid ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const = 0;
75. /**************************************************************************
76. * 功能描述:用它的大小和密度计算形状的质量
77. * 参数说明: massData : 计算形状的质量
78. density : 密度
79. * 返 回 值: (void)
80. ***************************************************************************/
81. virtualvoid ComputeMass(b2MassData* massData, float32 density) const = 0;
82. //形状类型
83. Type m_type;
84. //半径
85. float32 m_radius;
86. };
87. //获取形状类型
88. inline b2Shape::Type b2Shape::GetType() const
89. {
90. return m_type;
91. }
我们可以看到shape中基本上都是虚函数,是没有实现的。shape中定义了以下几个函数:
1、克隆一个形状
2、获取孩子形状类型
3、获取形状的孩子元素的数量
4、投射一束光到一个孩子形状中
5、计算一个孩子形状的轴对齐包围盒(aabb)
6、计算形状的质量
7、测试形状中点的密封性。
还有想问大家一点关于虚函数问题,为什么这里析构函数要定义为虚函数?如果不定义为虚函数又会怎样?有没有其它隐患?希望大家思考一下,有时间的话我们再说一下。
其它具体的定义请见注释,子形状的实现将于下篇文章与大家见面。各位,晚安。。。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
Box2d 2.2.1中父形状shape将有4个子形状,分别是b2EdgeShape(边缘形状)、b2ChainShape(链形状)、b2Circle(圆形状)和b2PolygonShape(多边形形状)。我们今天就来先看看边缘形状和链形状。
1、 边缘形状,边缘形状是由多条线段组成。不能与自己进行碰撞检测,只能与圆和多边形进行碰撞检测。在box2d中,如果要想检测两个形状是否碰撞,形状必须要有体积的,故边缘形状不能和边缘形状碰撞。我们就来看看b2EdgeShape的定义吧,打开b2EdgeShape.h文件。
[cpp]view plaincopy
1. // 一个线段(边缘)形状。这些可以连接在链或者环其他边缘形状。
2. //连接信息是用来确保正确的联系法线
3. class b2EdgeShape : public b2Shape
4. {
5. public:
6. /**************************************************************************
7. * 功能描述: 构造函数
8. * 参数说明: (void)
9. * 返 回 值: (void)
10. ***************************************************************************/
11. b2EdgeShape();
12. /**************************************************************************
13. * 功能描述: 设置独立的边缘
14. * 参数说明: v1 : 第一个顶点
15. v2 : 第二个顶点
16. * 返 回 值: (void)
17. ***************************************************************************/
18. void Set(const b2Vec2& v1, const b2Vec2& v2);
19. /**************************************************************************
20. * 功能描述:用soa块分配器克隆一个具体的形状
21. * 参数说明: allocator :soa分配器对象指针
22. * 返 回 值: 形状指针
23. ***************************************************************************/
24. b2Shape* Clone(b2BlockAllocator* allocator) const;
25. /**************************************************************************
26. * 功能描述:获取形状的孩子元素的数量
27. * 参数说明: (void)
28. * 返 回 值: 孩子元素的数量
29. ***************************************************************************/
30. int32 GetChildCount() const;
31. /**************************************************************************
32. * 功能描述:在这个形状中测试这个点的密封性,只适合用于凸的形状
33. * 参数说明: xf : 形状的变换
34. p : world坐标中的一个点
35. * 返 回 值: true : 密封
36. false:敞开
37. ***************************************************************************/
38. bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
39. /**************************************************************************
40. * 功能描述:投射一束光到一个孩子形状中
41. * 参数说明: output :输出光线投射的结果
42. input :输入光线投射
43. transform :变换应用到此形状中
44. childeIndex :孩子形状索引
45. * 返 回 值: true : 成功
46. false:失败
47. ***************************************************************************/
48. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
49. const b2Transform& transform, int32 childIndex) const;
50. /**************************************************************************
51. * 功能描述:给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
52. * 参数说明: aabb : 孩子形状的aabb指针
53. xf : 一个变换的引用
54. childIndex : 孩子的索引值
55. * 返 回 值: (void)
56. ***************************************************************************/
57. void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
58. /**************************************************************************
59. * 功能描述:用它的大小和密度计算形状的质量
60. * 参数说明: massData : 计算形状的质量
61. density : 密度
62. * 返 回 值: (void)
63. ***************************************************************************/
64. void ComputeMass(b2MassData* massData, float32 density) const;
65. // 边缘形状的顶点
66. b2Vec2 m_vertex1, m_vertex2;
67. // 可选的相邻的顶点,它们用于平滑碰撞
68. b2Vec2 m_vertex0, m_vertex3;
69. //是否含有相邻顶点
70. bool m_hasVertex0, m_hasVertex3;
71. };
72. //构造函数,用于初始化
73. inline b2EdgeShape::b2EdgeShape()
74. {
75. m_type = e_edge;
76. m_radius = b2_polygonRadius;
77. m_vertex0.x = 0.0f;
78. m_vertex0.y = 0.0f;
79. m_vertex3.x = 0.0f;
80. m_vertex3.y = 0.0f;
81. m_hasVertex0 = false;
82. m_hasVertex3 = false;
83. }
我们可以看到b2EdgeShape继承了b2Shape类,并对虚方法进行了重新定义。同时我们也实现了一个内联构造函数,进行对b2EdgeShape类进行初始化的时候使用。这方面,也不多说了,再来看看它的实现。
[cpp]view plaincopy
1. //设置独立的边缘
2. void b2EdgeShape::Set(const b2Vec2& v1, const b2Vec2& v2)
3. {
4. m_vertex1 = v1;
5. m_vertex2 = v2;
6. m_hasVertex0 = false;
7. m_hasVertex3 = false;
8. }
9.
10. //用soa块分配器克隆一个具体的形状
11. b2Shape* b2EdgeShape::Clone(b2BlockAllocator* allocator) const
12. {
13. //在内存池中申请一块内存
14. void* mem = allocator-<Allocate(sizeof(b2EdgeShape));
15. //在一块已存在的内存上分配对象
16. b2EdgeShape* clone = new (mem) b2EdgeShape;
17. *clone = *this;
18. return clone;
19. }
20. //获取形状的孩子元素的数量
21. int32 b2EdgeShape::GetChildCount() const
22. {
23. return 1;
24. }
25. //在这个形状中测试这个点的密封性,只适合用于凸的形状
26. bool b2EdgeShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const
27. {
28. // 消除编译器产生的警告
29. B2_NOT_USED(xf);
30. B2_NOT_USED(p);
31. returnfalse;
32. }
33. //光线投射
34. // p = p1 + t * d
35. // v = v1 + s * e
36. // p1 + t * d = v1 + s * e
37. // s * e – t * d = p1 – v1
38. bool b2EdgeShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
39. const b2Transform& xf, int32 childIndex) const
40. {
41. //消除编译器
42. B2_NOT_USED(childIndex);
43. //把光线投入到边缘框架的参考系中
44. //下面是根据给定的公式在计算,
45. //光线投射最近也在研究中,就不误导大家啦。。。
46. b2Vec2 p1 = b2MulT(xf.q, input.p1 – xf.p);
47. b2Vec2 p2 = b2MulT(xf.q, input.p2 – xf.p);
48. b2Vec2 d = p2 – p1;
49.
50. b2Vec2 v1 = m_vertex1;
51. b2Vec2 v2 = m_vertex2;
52. b2Vec2 e = v2 – v1;
53. b2Vec2 normal(e.y, -e.x);
54. normal.Normalize();
55.
56. // q = p1 + t * d
57. // dot(normal, q – v1) = 0
58. // dot(normal, p1 – v1) + t * dot(normal, d) = 0
59. float32 numerator = b2Dot(normal, v1 – p1);
60. float32 denominator = b2Dot(normal, d);
61.
62. if (denominator == 0.0f)
63. {
64. returnfalse;
65. }
66.
67. float32 t = numerator / denominator;
68. if (t < finputmaxFractiontspan>
69. {
70. returnfalse;
71. }
72.
73. b2Vec2 q = p1 + t * d;
74.
75. // q = v1 + s * r
76. // s = dot(q – v1, r) / dot(r, r)
77. b2Vec2 r = v2 – v1;
78. float32 rr = b2Dot(r, r);
79. if (rr == 0.0f)
80. {
81. returnfalse;
82. }
83.
84. float32 s = b2Dot(q – v1, r) / rr;
85. if (s < ffsspan>
86. {
87. returnfalse;
88. }
89.
90. output-<fraction = t;
91. if (numerator < 0.0f)
92. {
93. output-<normal = -normal;
94. }
95. else
96. {
97. output-<normal = normal;
98. }
99. returntrue;
100.}
101.//给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
102.void b2EdgeShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const
103.{
104.//消除编译器产生的警告
105. B2_NOT_USED(childIndex);
106.
107. b2Vec2 v1 = b2Mul(xf, m_vertex1);
108. b2Vec2 v2 = b2Mul(xf, m_vertex2);
109.
110. b2Vec2 lower = b2Min(v1, v2);
111. b2Vec2 upper = b2Max(v1, v2);
112.//重新设置aabb
113. b2Vec2 r(m_radius, m_radius);
114. aabb-<lowerBound = lower – r;
115. aabb-<upperBound = upper + r;
116.}
117.//计算质量
118.void b2EdgeShape::ComputeMass(b2MassData* massData, float32 density) const
119.{
120.//消除编译器产生的警告
121. B2_NOT_USED(density);
122.//设置用户数据
123. massData-<mass = 0.0f;
124. massData-<center = 0.5f * (m_vertex1 + m_vertex2);
125. massData-<I = 0.0f;
126.}
关于b2EdgeShape的实现,我们先来看下Clone函数,用SOA(小型对象分配器)分配了一块内存后,我们又用new在一块已存在的内存上分配对象,这是new的另外一种用法placement new,如果有童鞋对此不太理解或者想更深入研究的话,可以参照以下链接http://blog.sina.com.cn/s/blog_69e905cd0100k51b.html或http://club.topsage.com/thread-2467284-1-1.html,还有就是RayCast函数,主要就是根据注释上面的公式求解,然后就能获得结果。如果还有童鞋进一步了解的话,可以看《CollisionDetection inInteractive 3D Environments》这本书。当然我们通过上面代码可以简单的看到,b2EdgeShape是重量为零、密封性为假的形状。
2、 链形状,链形状是一个自由形式的序列的线段,链具有双面碰撞,故你可以使用内部或者外部碰撞。链形状提供了一种高效的方法来同时连接多条边,为你的游戏创造静态的游戏世界。链形状同时提供了创建链和环的方法,以便为大家提供想要的形状。链形状不能自身交叉,那样它有可能不能正常的工作。在Box2d中,链形状是通过b2ChainShape实现的。我们就来看看它是怎么做到的。
首先,我们看b2ChainShape.h文件。
[cpp]view plaincopy
1. //声明
2. class b2EdgeShape;
3.
4. // 链形状是一个自由形式的序列的线段,链具有双面碰撞,故你可以使用内部和外部碰撞
5. // 同时,你可以使用任何缠绕的顺序。尽管可能有很多顶点,不过它们都是通过b2Alloc分配的。
6. // 链接信息是用来创建平滑的碰撞
7. // 警告:如果他们自身交叉时,有可能不会正确的处理碰撞
8. // 即有可能正常工作,也有可能不正常工作
9. class b2ChainShape : public b2Shape
10. {
11. public:
12. /**************************************************************************
13. * 功能描述: 链形状构造函数
14. * 参数说明: (void)
15. * 返 回 值: (void)
16. ***************************************************************************/
17. b2ChainShape();
18. /**************************************************************************
19. * 功能描述: 析构函数,用b2Free释放多个点
20. * 参数说明: (void)
21. * 返 回 值: (void)
22. ***************************************************************************/
23. ~b2ChainShape();
24. /**************************************************************************
25. * 功能描述: 创建一个环。自动调整连接
26. * 参数说明: vertices : 顶点数组,拷贝它们
27. count : 顶点的数量
28. * 返 回 值: (void)
29. ***************************************************************************/
30. void CreateLoop(const b2Vec2* vertices, int32 count);
31. /**************************************************************************
32. * 功能描述: 用孤立的点创建一个链
33. * 参数说明: vertices : 顶点数组,拷贝它们
34. count : 顶点的数量
35. * 返 回 值: (void)
36. ***************************************************************************/
37. void CreateChain(const b2Vec2* vertices, int32 count);
38. /**************************************************************************
39. * 功能描述: 创建由一个顶点连接到上个顶点的链接
40. 不要在环中调用【即不能调用CreateLoop创建了环之后在调用此函数】
41. * 参数说明: prevVertex : 上一个顶点
42. * 返 回 值: (void)
43. ***************************************************************************/
44. void SetPrevVertex(const b2Vec2& prevVertex);
45. /**************************************************************************
46. * 功能描述: 创建由一个顶点连接到下个顶点的链接
47. 不要在环中调用【即不能调用CreateLoop创建了环之后在调用此函数】
48. * 参数说明: vertices : 下一个顶点
49. * 返 回 值: (void)
50. ***************************************************************************/
51. void SetNextVertex(const b2Vec2& nextVertex);
52. /**************************************************************************
53. * 功能描述: 用b2Alloc克隆所有顶点
54. 不要在环中调用【即不能调用CreateLoop创建了环之后在调用此函数】
55. * 参数说明: allocator : soa对象指针
56. * 返 回 值: 形状对象指针
57. ***************************************************************************/
58. b2Shape* Clone(b2BlockAllocator* allocator) const;
59. /**************************************************************************
60. * 功能描述:获取形状的孩子元素的数量
61. * 参数说明: (void)
62. * 返 回 值: 形状类型
63. ***************************************************************************/
64. int32 GetChildCount() const;
65.
66. /**************************************************************************
67. * 功能描述:根据索引值从链中获取指定的边缘形状
68. * 参数说明: edge :b2EdgeShape对象指针
69. index:索引值
70. * 返 回 值: (void)
71. ***************************************************************************/
72. void GetChildEdge(b2EdgeShape* edge, int32 index) const;
73. /**************************************************************************
74. * 功能描述:在这个形状中测试这个点的密封性,只适合用于凸的形状
75. * 参数说明: xf : 形状的变换
76. p : world坐标中的一个点
77. * 返 回 值: true : 密封
78. false:敞开
79. 总是返回false
80. ***************************************************************************/
81. bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
82. /**************************************************************************
83. * 功能描述:投射一束光到一个链形状中
84. * 参数说明: output :输出光线投射的结果
85. input :输入光线投射
86. transform :变换应用到此形状中
87. childeIndex :孩子形状索引
88. * 返 回 值: true : 成功
89. false:失败
90. ***************************************************************************/
91. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
92. const b2Transform& transform, int32 childIndex) const;
93. /**************************************************************************
94. * 功能描述:给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
95. 不要在环中调用【即不能调用CreateLoop创建了环之后在调用此函数】
96. * 参数说明: aabb : 孩子形状的aabb指针
97. xf : 一个变换的引用
98. childIndex : 孩子的索引值
99. * 返 回 值: (void)
100. ***************************************************************************/
101.void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
102./**************************************************************************
103. * 功能描述:用它的大小和密度计算形状的质量
104. Chains的质量为0
105. 不要在环中调用【即不能调用CreateLoop创建了环之后在调用此函数】
106. * 参数说明: massData : 计算形状的质量
107. density : 密度
108. * 返 回 值: (void)
109. ***************************************************************************/
110.void ComputeMass(b2MassData* massData, float32 density) const;
111.// 顶点数组
112. b2Vec2* m_vertices;
113.
114.//顶点个数
115. int32 m_count;
116.//前顶点,后顶点
117. b2Vec2 m_prevVertex, m_nextVertex;
118.//是否拥有前顶点,后顶点
119.bool m_hasPrevVertex, m_hasNextVertex;
120.};
121.//构造函数
122.inline b2ChainShape::b2ChainShape()
123.{
124. m_type = e_chain;
125. m_radius = b2_polygonRadius;
126. m_vertices = NULL;
127. m_count = 0;
128. m_hasPrevVertex = NULL;
129. m_hasNextVertex = NULL;
130.}
我们可以看到开始的时候,将b2EdgeShape类声明了一下,作为一个标签放入头文件中,在链接的时候,链接器会做相应的处理。【这里我们知道一下就行,不必深究】。当然,b2ChainShape类也继承了b2Shape类,并对父类的虚方法进行了重新定义和实现【当然是ccp文件啦】。构造函数同样也是初始化相关信息。
[cpp]view plaincopy
1. //析构函数
2. b2ChainShape::~b2ChainShape()
3. {
4. b2Free(m_vertices);
5. m_vertices = NULL;
6. m_count = 0;
7. }
8. //创建一个循环。自动调整连接
9. void b2ChainShape::CreateLoop(const b2Vec2* vertices, int32 count)
10. {
11. //验证是否没有顶点
12. b2Assert(m_vertices == NULL && m_count == 0);
13. //验证顶点的个数
14. b2Assert(count <= 3);
15. //在顶点个数基础上加1,
16. //用于将第一个元素放到最后一个空间中,
17. //这样就会形成一个环状,方便操作
18. m_count = count + 1;
19. //申请一个顶点动态数组,并拷贝旧数组中的数据到新空间中
20. m_vertices = (b2Vec2*)b2Alloc(m_count * sizeof(b2Vec2));
21. memcpy(m_vertices, vertices, count * sizeof(b2Vec2));
22. //申请一个
23. m_vertices[count] = m_vertices[0];
24. //前一个顶点、后一个顶点
25. m_prevVertex = m_vertices[m_count – 2];
26. m_nextVertex = m_vertices[1];
27. m_hasPrevVertex = true;
28. m_hasNextVertex = true;
29. }
30. //用孤立的点创建一个链
31. void b2ChainShape::CreateChain(const b2Vec2* vertices, int32 count)
32. {
33. //没有顶点
34. b2Assert(m_vertices == NULL && m_count == 0);
35. //顶点数要过大
36. b2Assert(count <= 2);
37. //
38. m_count = count;
39. //申请一个顶点动态数组,并拷贝旧数组中的数据到新空间中
40. m_vertices = (b2Vec2*)b2Alloc(count * sizeof(b2Vec2));
41. memcpy(m_vertices, vertices, m_count * sizeof(b2Vec2));
42. //没有左右节点
43. m_hasPrevVertex = false;
44. m_hasNextVertex = false;
45. }
46. //创建由一个顶点连接到上个顶点的链接
47. void b2ChainShape::SetPrevVertex(const b2Vec2& prevVertex)
48. {
49. m_prevVertex = prevVertex;
50. m_hasPrevVertex = true;
51. }
52. //创建由一个顶点连接到下个顶点的链接
53. void b2ChainShape::SetNextVertex(const b2Vec2& nextVertex)
54. {
55. m_nextVertex = nextVertex;
56. m_hasNextVertex = true;
57. }
58. //用b2Alloc克隆所有顶点
59. b2Shape* b2ChainShape::Clone(b2BlockAllocator* allocator) const
60. {
61. //申请一块内存
62. void* mem = allocator-<Allocate(sizeof(b2ChainShape));
63. //在一块已存在的内存上分配对象
64. b2ChainShape* clone = new (mem) b2ChainShape;
65. //创建链
66. clone-<CreateChain(m_vertices, m_count);
67. //获取相关信息,并返回
68. clone-<m_prevVertex = m_prevVertex;
69. clone-<m_nextVertex = m_nextVertex;
70. clone-<m_hasPrevVertex = m_hasPrevVertex;
71. clone-<m_hasNextVertex = m_hasNextVertex;
72. return clone;
73. }
74. //获取形状的孩子元素的数量
75. int32 b2ChainShape::GetChildCount() const
76. {
77. // edge count = vertex count – 1
78. return m_count – 1;
79. }
80. //根据索引值从链中获取指定的边缘形状
81. void b2ChainShape::GetChildEdge(b2EdgeShape* edge, int32 index) const
82. {
83. //验证索引值
84. b2Assert(0 < indexindexm_count-span>
85. //复制类型,半径
86. edge-<m_type = b2Shape::e_edge;
87. edge-<m_radius = m_radius;
88. //获取两端坐标
89. edge-<m_vertex1 = m_vertices[index + 0];
90. edge-<m_vertex2 = m_vertices[index + 1];
91.
92. //根据CreateLoop中可以知道,第一个元素和最后一个元素是一样的
93. //所以要单独判断
94. //判断索引值是否是第一个元素
95. if (index < 0)
96. {
97. edge-<m_vertex0 = m_vertices[index – 1];
98. edge-<m_hasVertex0 = true;
99. }
100.else
101. {
102. edge-<m_vertex0 = m_prevVertex;
103. edge-<m_hasVertex0 = m_hasPrevVertex;
104. }
105.//判断索引值是否是最后一个元素
106.if (index < m_count-span>
107. {
108. edge-<m_vertex3 = m_vertices[index + 2];
109. edge-<m_hasVertex3 = true;
110. }
111.else
112. {
113. edge-<m_vertex3 = m_nextVertex;
114. edge-<m_hasVertex3 = m_hasNextVertex;
115. }
116.}
117.//在这个形状中测试这个点的密封性,只适合用于凸的形状
118.bool b2ChainShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const
119.{
120. B2_NOT_USED(xf);
121. B2_NOT_USED(p);
122.returnfalse;
123.}
124.//投射一束光到一个链形状中
125.bool b2ChainShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
126.const b2Transform& xf, int32 childIndex) const
127.{
128.//验证孩子形状索引有效性
129. b2Assert(childIndex < m_countspan>
130.
131. b2EdgeShape edgeShape;
132.
133. int32 i1 = childIndex;
134. int32 i2 = childIndex + 1;
135.//最后一个元素,单独判断
136.if (i2 == m_count)
137. {
138. i2 = 0;
139. }
140.//初始化edgeShape
141. edgeShape.m_vertex1 = m_vertices[i1];
142. edgeShape.m_vertex2 = m_vertices[i2];
143.
144.return edgeShape.RayCast(output, input, xf, 0);
145.}
146.//计算顶点的上下边界
147.void b2ChainShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const
148.{
149.//验证孩子形状索引有效性
150. b2Assert(childIndex < m_countspan>
151.//获取坐标
152. int32 i1 = childIndex;
153. int32 i2 = childIndex + 1;
154.//最一个元素,单独判断
155.if (i2 == m_count)
156. {
157. i2 = 0;
158. }
159.//获取aabb的上下边界
160. b2Vec2 v1 = b2Mul(xf, m_vertices[i1]);
161. b2Vec2 v2 = b2Mul(xf, m_vertices[i2]);
162.
163. aabb-<lowerBound = b2Min(v1, v2);
164. aabb-<upperBound = b2Max(v1, v2);
165.}
166.//计算质量
167.void b2ChainShape::ComputeMass(b2MassData* massData, float32 density) const
168.{
169.//消除编译器警告
170. B2_NOT_USED(density);
171.//置空成员变量
172. massData-<mass = 0.0f;
173. massData-<center.SetZero();
174. massData-<I = 0.0f;
175.}
主要来看CreateLoop函数和CreateChain函数,它们的差别是很细微的,但是细微的差别带来的却是本质的变化,在CreateLoop函数中,我们申请了count+1顶点,比我们设置的要多出来一个顶点,这是干什么呢?主要是利用多申请的那个点的空间,放第一个顶点的信息,同时设置这两个点的前后顶点,并设置m_hasPrevVertex = true,和m_hasNextVertex = true,这样就形成了一个环了。而CreateChain函数则是直接申请了Count个顶点,并设置没有前后点。同时b2ChainShape也是零质量、密封性为假的形状。每两个点形成的形状就是边缘形状,我们可以通过GetChildEdge进行获取。其他的和前面的差不多,就不多说了。下一篇我们继续看剩下的两个形状。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
我们今天就来看看另外两个形状的实现。
1、圆形,有坐标和半径,(有点废话了,没有坐标和半径的能叫圆吗?)。圆形不能是空心的,必须是实心的。下面我们就来看看圆形是如何实现的。在b2CircleShape.h文件中,我们来看看源码。
[cpp]view plaincopy
1. // 圆形状 ,继承自b2Shape
2. class b2CircleShape : public b2Shape
3. {
4. public:
5. /**************************************************************************
6. * 功能描述:圆形构造函数
7. * 参数说明:(void)
8. * 返 回 值:(void)
9. ***************************************************************************/
10. b2CircleShape();
11. /**************************************************************************
12. * 功能描述:用soa块分配器克隆一个具体的形状【实现b2shape】
13. * 参数说明: allocator :soa分配器对象指针
14. * 返 回 值: (void)
15. ***************************************************************************/
16. b2Shape* Clone(b2BlockAllocator* allocator) const;
17. /**************************************************************************
18. * 功能描述:获取形状的子对象的数量
19. * 参数说明: (void)
20. * 返 回 值: 子对象数量
21. ***************************************************************************/
22. int32 GetChildCount() const;
23. /**************************************************************************
24. * 功能描述:在这个形状中测试这个点的密封性,只适合用于凸的形状
25. * 参数说明: xf : 形状的变换
26. p : world坐标中的一个点
27. * 返 回 值: true : 密封
28. false:敞开
29. ***************************************************************************/
30. bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
31. /**************************************************************************
32. * 功能描述:投射一束光到一个圆形状中
33. * 参数说明: output :输出光线投射的结果
34. input :输入光线投射
35. transform :变换应用到此形状中
36. childeIndex :孩子形状索引
37. * 返 回 值:?++? true : 成功
38. false:失败
39. ***************************************************************************/
40. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
41. const b2Transform& transform, int32 childIndex) const;
42. /**************************************************************************
43. * 功能描述:给出一个变换,计算一个圆形状的轴对齐包围盒(aabb)
44. * 参数说明: aabb : 孩子形状的aabb指针
45. xf : 一个变换的引用
46. childIndex : 孩子的索引值
47. * 返 回 值: true : 成功
48. false:失败
49. ***************************************************************************/
50. void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
51.
52. /**************************************************************************
53. * 功能描述:用它的大小和密度计算形状的质量
54. * 参数说明: massData : 计算形状的质量
55. density : 密度
56. * 返 回 值: (void)
57. ***************************************************************************/
58. void ComputeMass(b2MassData* massData, float32 density) const;
59. /**************************************************************************
60. * 功能描述:根据既定的方向,得到支撑顶点索引
61. 圆形没有顶点,故永远返回0
62. * 参数说明: d :二维列向量
63. * 返 回 值: 0
64. ***************************************************************************/
65. int32 GetSupport(const b2Vec2& d) const;
66. /**************************************************************************
67. * 功能描述:根据既定的方向,得到支持的顶点
68. 获取支撑点索引,圆形没有顶点,故永远返回0
69. * 参数说明: d :二维列向量
70. * 返 回 值: 0
71. ***************************************************************************/
72. const b2Vec2& GetSupportVertex(const b2Vec2& d) const;
73. /**************************************************************************
74. * 功能描述:获取顶点数量
75. * 参数说明:(void)
76. * 返 回 值: 顶点数量
77. ***************************************************************************/
78. int32 GetVertexCount() const { return 1; }
79. /**************************************************************************
80. * 功能描述:通过索引获得一个顶点,用于b2Distance
81. * 参数说明:(void)
82. * 返 回 值: 坐标点
83. ***************************************************************************/
84. const b2Vec2& GetVertex(int32 index) const;
85. //坐标点
86. b2Vec2 m_p;
87. };
88. //构造函数
89. inline b2CircleShape::b2CircleShape()
90. {
91. m_type = e_circle;
92. m_radius = 0.0f;
93. m_p.SetZero();
94. }
95. //获取支撑点索引,圆形没有顶点,故永远返回0
96. inline int32 b2CircleShape::GetSupport(const b2Vec2 &d) const
97. {
98. B2_NOT_USED(d);
99. return 0;
100.}
101.//获取支撑点数量
102.inlineconst b2Vec2& b2CircleShape::GetSupportVertex(const b2Vec2 &d) const
103.{
104. B2_NOT_USED(d);
105.return m_p;
106.}
107.//通过索引获得一个顶点,用于b2Distance
108.inlineconst b2Vec2& b2CircleShape::GetVertex(int32 index) const
109.{
110. B2_NOT_USED(index);
111. b2Assert(index == 0);
112.return m_p;
113.}
圆形依然是继承自b2Shape,对虚函数进行了重新定义和实现。也许有人会注意到还有三个内联函数是上两个形状所没有的,关于它有什么用,我们到b2Distance那部分时会进一步说明,现在留意一下就行。
关于b2CircleShape的实现,我们看下面代码就可:
[cpp]view plaincopy
1. //用soa块分配器克隆一个具体的形状【实现b2shape】
2. b2Shape* b2CircleShape::Clone(b2BlockAllocator* allocator) const
3. {
4. void* mem = allocator-<Allocate(sizeof(b2CircleShape));
5. b2CircleShape* clone = new (mem) b2CircleShape;
6. *clone = *this;
7. return clone;
8. }
9. //获取形状的子对象的数量
10. int32 b2CircleShape::GetChildCount() const
11. {
12. return 1;
13. }
14. //在这个形状中测试这个点的密封性,只适合用于凸的形状
15. bool b2CircleShape::TestPoint(const b2Transform& transform, const b2Vec2& p) const
16. {
17. b2Vec2 center = transform.p + b2Mul(transform.q, m_p);
18. b2Vec2 d = p – center;
19. return b2Dot(d, d) < m_radiusm_radiusspan>
20. }
21.
22.
23. // 《Collision Detection in Interactive 3D Environments》 by Gino van den Bergen
24. // 在3.1.2节,【从 http://download.csdn.net/detail/cg0206/4875309 下载】
25. // x = s + a * r
26. // norm(x) = radius
27. // (关于norm函数,请看 http://zh.wikipedia.org/wiki/范数 或
28. // http://en.wikipedia.org/wiki/Norm_(mathematics) )
29. bool b2CircleShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
30. const b2Transform& transform, int32 childIndex) const
31. {
32. //消除警告
33. B2_NOT_USED(childIndex);
34. //
35. b2Vec2 position = transform.p + b2Mul(transform.q, m_p);
36. b2Vec2 s = input.p1 – position;
37. float32 b = b2Dot(s, s) – m_radius * m_radius;
38.
39. // 解决二元方程
40. b2Vec2 r = input.p2 – input.p1;
41. float32 c = b2Dot(s, r);
42. float32 rr = b2Dot(r, r);
43. float32 sigma = c * c – rr * b;
44. //检测负的判别式和短线段
45. if (sigma < frrb_epsilonspan>
46. {
47. returnfalse;
48. }
49. //找到线与圆的交叉点
50. float32 a = -(c + b2Sqrt(sigma));
51. // 判断交叉点是否在线段上
52. if (0.0f < aa=” input.maxFraction * rr)
53. {
54. a /= rr;
55. output-<fraction = a;
56. output-<normal = s + a * r;
57. output-<normal.Normalize();
58. returntrue;
59. }
60.
61. returnfalse;
62. }
63.
64. //给出一个变换,计算一个圆形状的轴对齐包围盒(aabb)
65. void b2CircleShape::ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const
66. {
67. //消除警告
68. B2_NOT_USED(childIndex);
69. //获取变换后的变量,并重新设置aabb
70. b2Vec2 p = transform.p + b2Mul(transform.q, m_p);
71. aabb-<lowerBound.Set(p.x – m_radius, p.y – m_radius);
72. aabb-<upperBound.Set(p.x + m_radius, p.y + m_radius);
73. }
74. //用它的大小和密度计算形状的质量
75. void b2CircleShape::ComputeMass(b2MassData* massData, float32 density) const
76. {
77. //获取形状的质量、质心
78. massData-<mass = density * b2_pi * m_radius * m_radius;
79. massData-<center = m_p;
80. // 惯量相对于本地原点
81. massData-<I = massData-<mass * (0.5f * m_radius * m_radius + b2Dot(m_p, m_p));
82. }
这次我们可以看到圆形是有质量、密封性为真的形状。剩下的就不多说了。看注释哈。
2、多边形
我们这里说的多边形是凸多边形,所谓凸多边形就是一条直线与该类多边形相交交点最多不超过两个。同时还是实心的,不能是空心。多边形的顶点范围应该在[3,b2_maxPolygonVertices]内,box2d中定义为8个,你也可以在公共模块的b2Settings.h文件中修改它的值,不过一般不建议那么做。好了,我们就来看看它的实现。
看b2PolygonShape.h文件。
[cpp]view plaincopy
1. //一个凸多边形
2. // 多边形最多有b2_maxPolygonVertices个顶点
3. //在大多数情况下你不需要具有太多顶点的多边形
4. class b2PolygonShape : public b2Shape
5. {
6. public:
7. /**************************************************************************
8. * 功能描述:多边形构造函数,初始化数据
9. * 参数说明: (void)
10. * 返 回 值: (void)
11. ***************************************************************************/
12. b2PolygonShape();
13. /**************************************************************************
14. * 功能描述:用soa块分配器克隆一个具体的形状
15. * 参数说明: allocator :soa分配器对象指针
16. * 返 回 值: (void)
17. ***************************************************************************/
18. b2Shape* Clone(b2BlockAllocator* allocator) const;
19.
20. /**************************************************************************
21. * 功能描述:获取孩子形状个数,你可以使用它去创建形状
22. * 参数说明: (void)
23. * 返 回 值: 孩子形状个数
24. ***************************************************************************/
25. int32 GetChildCount() const;
26. /**************************************************************************
27. * 功能描述:复制顶点。假定所有的顶点定义了一个凸多边形
28. 它假定了外部每一个边都是从右边开始的
29. 顶点数在【3,b2_maxPolygonVertices】之间
30. * 参数说明: vertices :顶点数组
31. vertexCount :顶点数量
32. * 返 回 值: (void)
33. ***************************************************************************/
34. void Set(const b2Vec2* vertices, int32 vertexCount);
35. /**************************************************************************
36. * 功能描述:构建一个包围着顶点轴对齐盒子
37. * 参数说明: hx :半宽
38. hy :半高
39. * 返 回 值: (void)
40. ***************************************************************************/
41. void SetAsBox(float32 hx, float32 hy);
42. /**************************************************************************
43. * 功能描述:构建一个包围着顶点确定方位的轴对齐盒子
44. * 参数说明: hx :半宽
45. hy :半高
46. center:盒子的中心点
47. angle :盒子的旋转角度
48. * 返 回 值: (void)
49. ***************************************************************************/
50. void SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle);
51.
52. /**************************************************************************
53. * 功能描述:在这个形状中测试这个点的密封性,只适合用于凸的形状
54. * 参数说明: xf : 形状的变换
55. p : world坐标中的一个点
56. * 返 回 值: true : 密封
57. false:敞开
58. ***************************************************************************/
59. bool TestPoint(const b2Transform& transform, const b2Vec2& p) const;
60.
61. /**************************************************************************
62. * 功能描述:投射一束光到一个孩子形状中
63. * 参数说明: output :输出光线投射的结果
64. input :输入光线投射
65. transform :变换应用到此形状中
66. childeIndex :孩子形状索引
67. * 返 回 值: true : 成功
68. false:失败
69. ***************************************************************************/
70. bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
71. const b2Transform& transform, int32 childIndex) const;
72.
73. /**************************************************************************
74. * 功能描述:给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
75. * 参数说明: aabb : 孩子形状的aabb指针
76. xf : 一个变换的引用
77. childIndex : 孩子的索引值
78. * 返 回 值: (void)
79. ***************************************************************************/
80. void ComputeAABB(b2AABB* aabb, const b2Transform& transform, int32 childIndex) const;
81.
82. /**************************************************************************
83. * 功能描述:用它的大小和密度计算形状的质量
84. * 参数说明: massData : 计算形状的质量
85. density : 密度
86. * 返 回 值: (void)
87. ***************************************************************************/
88. void ComputeMass(b2MassData* massData, float32 density) const;
89. /**************************************************************************
90. * 功能描述:获取顶点数量
91. * 参数说明: (void)
92. * 返 回 值: 顶点数量
93. ***************************************************************************/
94. int32 GetVertexCount() const { return m_vertexCount; }
95. /**************************************************************************
96. * 功能描述:根据顶点索引获取一个顶点
97. * 参数说明: index :索引值
98. * 返 回 值: 顶点
99. ***************************************************************************/
100.const b2Vec2& GetVertex(int32 index) const;
101.//重心坐标
102. b2Vec2 m_centroid;
103.//顶点坐标数组
104. b2Vec2 m_vertices[b2_maxPolygonVertices];
105.//法线数组
106. b2Vec2 m_normals[b2_maxPolygonVertices];
107.//顶点数量
108. int32 m_vertexCount;
109.};
110.
111.//多边形构造函数,初始化数据
112.inline b2PolygonShape::b2PolygonShape()
113.{
114. m_type = e_polygon;
115. m_radius = b2_polygonRadius;
116. m_vertexCount = 0;
117. m_centroid.SetZero();
118.}
119.//根据顶点索引获取一个顶点
120.inlineconst b2Vec2& b2PolygonShape::GetVertex(int32 index) const
121.{
122.//验证index的有效性
123. b2Assert(0 < indexindexm_vertexCountspan>
124.return m_vertices[index];
125.}
同样,多边形b2PolygonShape还是继承自b2Shape。b2PolygonShape也对父类中的虚函数进行了定义和实现。关于内联函数也不多说了,我们来看b2PolygonShape的具体实现吧,(有点小激动。。。嘿嘿)看b2PolygonShape.ccp文件。
[cpp]view plaincopy
1. //用soa块分配器克隆一个具体的形状
2. b2Shape* b2PolygonShape::Clone(b2BlockAllocator* allocator) const
3. {
4. //在内存池中申请一块内存
5. void* mem = allocator-<Allocate(sizeof(b2PolygonShape));
6. //在一块已存在的内存上分配对象
7. //这是new的另外一种用法placement new
8. //参照以下链接 http://blog.sina.com.cn/s/blog_69e905cd0100k51b.html
9. //或 http://club.topsage.com/thread-2467284-1-1.html
10. b2PolygonShape* clone = new (mem) b2PolygonShape;
11. *clone = *this;
12. return clone;
13. }
14. //构建一个包围着顶点轴对齐盒子
15. void b2PolygonShape::SetAsBgox(float32 hx, float32 hy)
16. {
17. //初始化盒子顶点坐标
18. m_vertexCount = 4;
19. m_vertices[0].Set(-hx, -hy);
20. m_vertices[1].Set( hx, -hy);
21. m_vertices[2].Set( hx, hy);
22. m_vertices[3].Set(-hx, hy);
23. m_normals[0].Set(0.0f, -1.0f);
24. m_normals[1].Set(1.0f, 0.0f);
25. m_normals[2].Set(0.0f, 1.0f);
26. m_normals[3].Set(-1.0f, 0.0f);
27. m_centroid.SetZero();
28. }
29. //构建一个包围着顶点确定方位的轴对齐盒子
30. void b2PolygonShape::SetAsBox(float32 hx, float32 hy, const b2Vec2& center, float32 angle)
31. {
32. //初始化盒子顶点坐标
33. m_vertexCount = 4;
34. m_vertices[0].Set(-hx, -hy);
35. m_vertices[1].Set( hx, -hy);
36. m_vertices[2].Set( hx, hy);
37. m_vertices[3].Set(-hx, hy);
38. m_normals[0].Set(0.0f, -1.0f);
39. m_normals[1].Set(1.0f, 0.0f);
40. m_normals[2].Set(0.0f, 1.0f);
41. m_normals[3].Set(-1.0f, 0.0f);
42. m_centroid = center;
43. //创建一个变换
44. b2Transform xf;
45. xf.p = center;
46. xf.q.Set(angle);
47. // 变换顶点和法线
48. for (int32 i = 0; i < m_vertexCountispan>
49. {
50. m_vertices[i] = b2Mul(xf, m_vertices[i]);
51. m_normals[i] = b2Mul(xf.q, m_normals[i]);
52. }
53. }
54. //获取孩子形状个数,你可以使用它去创建形状
55. int32 b2PolygonShape::GetChildCount() const
56. {
57. return 1;
58. }
59. /**************************************************************************
60. * 功能描述:计算重心坐标
61. 参见 http://www.cnblogs.com/jun930123/archive/2012/09/10/2678305.html
62. * 参数说明: vs :顶点数组
63. count:顶点数
64. * 返 回 值: 重心坐标
65. ***************************************************************************/
66. static b2Vec2 ComputeCentroid(const b2Vec2* vs, int32 count)
67. {
68. b2Assert(count <= 3);
69.
70. b2Vec2 c; c.Set(0.0f, 0.0f);
71. float32 area = 0.0f;
72. //三角形的一个参考点
73. //它的位置并不能改变结构(除了舍入误差)
74. b2Vec2 pRef(0.0f, 0.0f);
75. #if 0
76. // This code would put the reference point inside the polygon.
77. for (int32 i = 0; i < countispan>
78. {
79. pRef += vs[i];
80. }
81. pRef *= 1.0f / count;
82. #endif
83.
84. const float32 inv3 = 1.0f / 3.0f;
85.
86. for (int32 i = 0; i < countispan>
87. {
88. // 三角形顶点
89. b2Vec2 p1 = pRef;
90. b2Vec2 p2 = vs[i];
91. b2Vec2 p3 = i + 1 < countvsivsspan>
92. //三角形两边的向量
93. b2Vec2 e1 = p2 – p1;
94. b2Vec2 e2 = p3 – p1;
95. //计算三角形面积
96. // S = 1/2 * b2Cross(e1,e2)
97. float32 D = b2Cross(e1, e2);
98. float32 triangleArea = 0.5f * D;
99. //获取面积之后
100. area += triangleArea;
101.// 面积加权重心
102. c += triangleArea * inv3 * (p1 + p2 + p3);
103. }
104.//重心
105. b2Assert(area < b2_epsilon);
106. c *= 1.0f / area;
107.return c;
108.}
109.//复制顶点
110.void b2PolygonShape::Set(const b2Vec2* vertices, int32 count)
111.{
112.//验证顶点的个数
113. b2Assert(3 < countcount=” b2_maxPolygonVertices);
114. m_vertexCount = count;
115.
116.//拷贝顶点
117.for (int32 i = 0; i < m_vertexCountispan>
118. {
119. m_vertices[i] = vertices[i];
120. }
121.// 计算法线,确保每条边的长度都不为0
122.for (int32 i = 0; i < m_vertexCountispan>
123. {
124.//获取两个相邻点的索引
125. int32 i1 = i;
126. int32 i2 = i + 1 < m_vertexCountispan>
127.//获得边向量
128. b2Vec2 edge = m_vertices[i2] – m_vertices[i1];
129. b2Assert(edge.LengthSquared() < b2_epsilon * b2_epsilon);
130.//计算法线,并标准化
131. m_normals[i] = b2Cross(edge, 1.0f);
132. m_normals[i].Normalize();
133. }
134.
135.#ifdef _DEBUG
136.//确保多边形是凸的和内部边是从左到右的
137.for (int32 i = 0; i < m_vertexCountispan>
138. {
139. int32 i1 = i;
140. int32 i2 = i + 1 < m_vertexCountispan>
141. b2Vec2 edge = m_vertices[i2] – m_vertices[i1];
142.
143.for (int32 j = 0; j < m_vertexCountjspan>
144. {
145.//不检查当前边上的顶点
146.if (j == i1 || j == i2)
147. {
148.continue;
149. }
150.
151. b2Vec2 r = m_vertices[j] – m_vertices[i1];
152.//如果这个崩溃,你的多边形不是凸多边形,含有共线的边或者缠绕的顺序不对
153. float32 s = b2Cross(edge, r);
154. b2Assert(s < 0.0f && “ERROR: Please ensure your polygon is convex and has a CCW winding order”);
155. }
156. }
157.#endif
158.//计算重心
159. m_centroid = ComputeCentroid(m_vertices, m_vertexCount);
160.}
161.//在这个形状中测试这个点的密封性,只适合用于凸的形状
162.bool b2PolygonShape::TestPoint(const b2Transform& xf, const b2Vec2& p) const
163.{
164.//转置
165. b2Vec2 pLocal = b2MulT(xf.q, p – xf.p);
166.//遍历整个顶点,便用
167.for (int32 i = 0; i < m_vertexCountispan>
168. {
169.//判断点积
170. float32 dot = b2Dot(m_normals[i], pLocal – m_vertices[i]);
171.if (dot < 0.0f)
172. {
173.returnfalse;
174. }
175. }
176.
177.returntrue;
178.}
179.//光线投射
180.bool b2PolygonShape::RayCast(b2RayCastOutput* output, const b2RayCastInput& input,
181.const b2Transform& xf, int32 childIndex) const
182.{
183.//消除编译器发出的警告
184. B2_NOT_USED(childIndex);
185.//把光线放进多边形的参照系中
186. b2Vec2 p1 = b2MulT(xf.q, input.p1 – xf.p);
187. b2Vec2 p2 = b2MulT(xf.q, input.p2 – xf.p);
188. b2Vec2 d = p2 – p1;
189.//
190. float32 lower = 0.0f, upper = input.maxFraction;
191.
192. int32 index = -1;
193.//遍历所有节点
194.for (int32 i = 0; i < m_vertexCountispan>
195. {
196.// p = p1 + a * d
197.// dot(normal, p – v) = 0
198.// dot(normal, p1 – v) + a * dot(normal, d) = 0
199. float32 numerator = b2Dot(m_normals[i], m_vertices[i] – p1);
200. float32 denominator = b2Dot(m_normals[i], d);
201.
202.if (denominator == 0.0f)
203. {
204.if (numerator < fspan>
205. {
206.returnfalse;
207. }
208. }
209.else
210. {
211.// 注意:
212.// lower < numeratordenominatordenominatorspan>
213.// 当 denominator <0 span>
214.// lower < numeratordenominator=”=” >denominator * lower < numerator.
215.if (denominator < fnumeratorlowerdenominatorspan>
216. {
217.// 增加 lower
218.// 进入这半段
219. lower = numerator / denominator;
220. index = i;
221. }
222.elseif (denominator < 0.0f && numerator < upperdenominatorspan>
223. {
224.//减小 upper
225.//进入余下的半段
226. upper = numerator / denominator;
227. }
228. }
229.//有时候lower会有最小值b2_epsilon的误差,引起断言。
230.//虽然表面上用最小值b2_epsilon可以使边缘形状工作,但是现在我们将单独处理
231.//if (upper < lower-b_epsilonspan>
232.if (upper < lowerspan>
233. {
234.returnfalse;
235. }
236. }
237.//验证lower有效性
238. b2Assert(0.0f < lowerlower=” input.maxFraction);
239.//
240.if (index <= 0)
241. {
242. output-<fraction = lower;
243. output-<normal = b2Mul(xf.q, m_normals[index]);
244.returntrue;
245. }
246.
247.returnfalse;
248.}
249.//给出一个变换,计算一个孩子形状的轴对齐包围盒(aabb)
250.void b2PolygonShape::ComputeAABB(b2AABB* aabb, const b2Transform& xf, int32 childIndex) const
251.{
252. B2_NOT_USED(childIndex);
253.//获取上下界限
254. b2Vec2 lower = b2Mul(xf, m_vertices[0]);
255. b2Vec2 upper = lower;
256.//变量所有顶点
257.for (int32 i = 1; i < m_vertexCountispan>
258. {
259. b2Vec2 v = b2Mul(xf, m_vertices[i]);
260. lower = b2Min(lower, v);
261. upper = b2Max(upper, v);
262. }
263.//设置aabb
264. b2Vec2 r(m_radius, m_radius);
265. aabb-<lowerBound = lower – r;
266. aabb-<upperBound = upper + r;
267.}
268.//计算形状质量
269.void b2PolygonShape::ComputeMass(b2MassData* massData, float32 density) const
270.{
271.//多边形质量(mass)、重心(centroid)、惯量(inertia)
272.//ρ是多边形密度,也是在单位面积上的质量。
273.//然后 质量mass = ρ* int(dA);
274.//centroid.x = (1/mass) * ρ*int(x * dA)
275.//centroid.y = (1/mass) * ρ*int(x * dB)
276.//I = ρ*int((x*x+y*y) * dA)
277.//我们可以计算这些部分,将凸多边形每个部分的三角形加起来。
278.//求每部分的三角形,我们可以列一个关于三角形(u,v)坐标的二元方程式
279.// x = x0 + e1x * u + e2x * v
280.// y = y0 + e1y * u + e2y * v
281.//其中 0 < u=” v && u + v <= 1
282.//我们将求的 u、v均在[0,1]之间
283.//我们也用到两个向量的叉积【求三角形面积用】:D = cross(e1,e2)
284.//三角形重心 centroid = (1/3) * (p1 + p2 + p3)
285.//其余的推导是计算机代数啦。。。
286. b2Assert(m_vertexCount <= 3);
287.
288. b2Vec2 center; center.Set(0.0f, 0.0f);
289. float32 area = 0.0f;
290. float32 I = 0.0f;
291.//s是每个形成的三角形的参考点
292.//它的位置不会影响结果(除了舍入的误差)
293. b2Vec2 s(0.0f, 0.0f);
294.//将参考点s放入多边形内部
295.for (int32 i = 0; i < m_vertexCountispan>
296. {
297. s += m_vertices[i];
298. }
299. s *= 1.0f / m_vertexCount;
300.
301.const float32 k_inv3 = 1.0f / 3.0f;
302.
303.for (int32 i = 0; i < m_vertexCountispan>
304. {
305.//三角形顶点
306. b2Vec2 e1 = m_vertices[i] – s;
307. b2Vec2 e2 = i + 1 < m_vertexCountm_verticesi-sm_vertices-sspan>
308.
309. float32 D = b2Cross(e1, e2);
310.
311. float32 triangleArea = 0.5f * D;
312. area += triangleArea;
313.// 面积加权重心
314. center += triangleArea * k_inv3 * (e1 + e2);
315.
316. float32 ex1 = e1.x, ey1 = e1.y;
317. float32 ex2 = e2.x, ey2 = e2.y;
318.
319. float32 intx2 = ex1*ex1 + ex2*ex1 + ex2*ex2;
320. float32 inty2 = ey1*ey1 + ey2*ey1 + ey2*ey2;
321.
322. I += (0.25f * k_inv3 * D) * (intx2 + inty2);
323. }
324.//总计质量
325. massData-<mass = density * area;
326.//质心,一般也指重心,从概念上来说是有差别的,但通常我们用混淆了。
327. b2Assert(area < b2_epsilon);
328. center *= 1.0f / area;
329. massData-<center = center + s;
330.
331.//转动惯量相对于当前的原点(s点)
332. massData-<I = density * I;
333.// 移动质心到原始的物体原点
334. massData-<I += massData-<mass * (b2Dot(massData-<center, massData-<center) – b2Dot(center, center));
335.}
这段代码中,我们来看看SetAsBox(float32 hx,float32 hy)函数,注意一下我们传入的两个参数分别是这个盒子的宽度的一半和高度的一半,再者就是初始化4个顶点并设置质心。在看看ComputeCentroid函数,我们可以知道这可以获取重心的坐标,这里也可以说是获取质心的坐标,有人要问了,难道质心和重心是一样的吗?答案很明显不是一样的,要不然就不会在同一个物理学科中整出两个概念来了。不过,Box2d中假设质量分布和重力分布是一致的,所以两者是重合的。关于这方面的知识可以百度或者谷歌。点击“多边形重心”将会看到如何求解的博客。
再看设置一个多边形。Set函数通过给定的顶点数组和顶点数量,我们进行连接(按逆时针方向)并判断是否是凸多边形。如不是的话则断言。若是则继续初始化。
对于RayCast函数,还是计算公式,关于公式如何得到的,参照本书《CollisionDetection inInteractive 3D Environments》。计算多边形质量的函数ComputeMass注释里面有详细的解说,我们也不多说了。同时我们可以看到b2PolygonShape形状有质量、密封性为真的形状。
Ok,形状部分。。。oh,on,还有一个问题,没有解决,My God。大家还记得是什么问题吗?
(都知道!!!好吧,我错了。)
下面我们就来说说,在b2Shape类中如果析构函数不是虚函数的话,当我们申请一个父类(b2Shape)指针指向子类对象(如b2CircleShape)后,再来释放父类指针占用的内存的时候,只有父类的析构函数被调用,而子类的析构函数则不会被调用,这就可能引起内存泄漏。如果是父类析构是虚函数的话,则会自低而上的依次调用。
这次真的好了,形状部分,我们将暂时告一段落,晚安,明天还要上班,早点睡啦。。。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
Box2d中距离是指两个形状最近点之间的距离,主要用于形状的碰撞检测,通过GJK算法实现,在GJK中又使用voroni区域算法和重心坐标来完成的。在Box2d最终调用b2Ditance方法来求得距离。使用此方法需要将两个形状转换成一个b2DistanceProxy。为了提供高内部调用的效率,在外部开始调用b2Distance方法时,内部做了一个缓冲,用来提高计算距离的效率。关于GJK、voronic区域算法和重心坐标,大家可以点击对应的红色字体。
我们将不在文章中分析这些算法了,感兴趣的童鞋自行深入的研究。不罗嗦了,我们来看看它的定义,上代码:
[cpp]view plaincopy
1. //声明类
2. class b2Shape;
3. // 距离代理,使用GJK算法实现
4. // 它封装了任何形状
5. struct b2DistanceProxy
6. {
7. /**************************************************************************
8. * 功能描述:构造函数,初始化信息
9. * 参数说明: (void)
10. * 返 回 值: (void)
11. ***************************************************************************/
12. b2DistanceProxy() : m_vertices(NULL), m_count(0), m_radius(0.0f) {}
13. /**************************************************************************
14. * 功能描述:用给定的形状初始化代理。当使用代理时,形状必须保持在一定的范围内
15. * 参数说明: shape:形状指针变量
16. index:索引值
17. * 返 回 值: (void)
18. ***************************************************************************/
19. void Set(const b2Shape* shape, int32 index);
20. /**************************************************************************
21. * 功能描述:根据指定的方向获得支撑点的索引
22. * 参数说明: d:向量的引用
23. * 返 回 值: 顶点的索引
24. ***************************************************************************/
25. int32 GetSupport(const b2Vec2& d) const;
26. /**************************************************************************
27. * 功能描述:根据指定的方向获得支撑点
28. * 参数说明: d:向量的引用
29. * 返 回 值: 顶点坐标
30. ***************************************************************************/
31. const b2Vec2& GetSupportVertex(const b2Vec2& d) const;
32. /**************************************************************************
33. * 功能描述:获取顶点个数
34. * 参数说明: (void)
35. * 返 回 值: 顶点的个数
36. ***************************************************************************/
37. int32 GetVertexCount() const;
38. /**************************************************************************
39. * 功能描述:根据索引获得顶点
40. 用于b2Distance
41. * 参数说明: index:索引
42. * 返 回 值: 顶点
43. ***************************************************************************/
44. const b2Vec2& GetVertex(int32 index) const;
45. //顶点缓存,用于保存chain形状中的两个顶点
46. b2Vec2 m_buffer[2];
47. //坐标点
48. const b2Vec2* m_vertices;
49. //顶点的数量
50. int32 m_count;
51. //半径
52. float32 m_radius;
53. };
54.
55. //第一次调用将count置0
56. struct b2SimplexCache
57. {
58. float32 metric; //长度或面积
59. uint16 count; //顶点数
60. uint8 indexA[3]; //shapeA上的顶点索引数组
61. uint8 indexB[3]; //shapeB上的顶点索引数组
62. };
63. // b2Distance的输入
64. // 在计算中你必须选择使用形状的半径
65. struct b2DistanceInput
66. {
67. b2DistanceProxy proxyA; //距离代理A
68. b2DistanceProxy proxyB; //距离代理B
69. b2Transform transformA; //转换A
70. b2Transform transformB; //转换B
71. bool useRadii; //是否使用半径
72. };
73.
74. // b2Distance的输出
75. struct b2DistanceOutput
76. {
77. b2Vec2 pointA; //shapeA上最近的点
78. b2Vec2 pointB; //shapeB上最近的点
79. float32 distance; //距离
80. int32 iterations; //GJK的迭代次数
81. };
82.
83. /**************************************************************************
84. * 功能描述:在两个形状间计算最近点。支持下面的任何组合:
85. 圆形、多边形、边缘形状。单纯形缓存输入/输出
86. 第一次调用设置b2SimplexCache.count为0
87. * 参数说明: output :距离输出指针
88. cache :单纯形缓存指针
89. input :距离输入指针
90. * 返 回 值: (void)
91. ***************************************************************************/
92. void b2Distance(b2DistanceOutput* output,
93. b2SimplexCache* cache,
94. const b2DistanceInput* input);
95.
96.
97. //////////////////////////////////////////////////////////////////////////
98. //获取顶点个数
99. inline int32 b2DistanceProxy::GetVertexCount() const
100.{
101.return m_count;
102.}
103.//根据索引获得顶点
104.inlineconst b2Vec2& b2DistanceProxy::GetVertex(int32 index) const
105.{
106. b2Assert(0 < indexindexm_countspan>
107.return m_vertices[index];
108.}
109.//根据指定的方向获得支撑点索引,用于构建单纯形
110.inline int32 b2DistanceProxy::GetSupport(const b2Vec2& d) const
111.{
112. int32 bestIndex = 0;
113.// 获取最远的点,用于产生的单纯形包含最大的空间区域
114. float32 bestValue = b2Dot(m_vertices[0], d);
115.//遍历所有的顶点,获取最远点的索引
116.for (int32 i = 1; i < m_countispan>
117. {
118. float32 value = b2Dot(m_vertices[i], d);
119.if (value < bestValue)
120. {
121. bestIndex = i;
122. bestValue = value;
123. }
124. }
125.//返回索引
126.return bestIndex;
127.}
128.//根据指定的方向向量获得支撑点,用于构建单纯形
129.inlineconst b2Vec2& b2DistanceProxy::GetSupportVertex(const b2Vec2& d) const
130.{
131. int32 bestIndex = 0;
132. float32 bestValue = b2Dot(m_vertices[0], d);
133.//遍历所有的顶点,获得远的点
134.for (int32 i = 1; i < m_countispan>
135. {
136. float32 value = b2Dot(m_vertices[i], d);
137.if (value < bestValue)
138. {
139. bestIndex = i;
140. bestValue = value;
141. }
142. }
143.//返回顶点
144.return m_vertices[bestIndex];
145.}
可以看到,我们在这里定义了一个距离代理b2DistanceProxy,使用了GJK算法,同时它封装了任何形状。我们先在此注意一下m_buffer[2]这个变量,它专门保存链条形状而弄的。等到具体实现部分,我们再讨论。再看看b2Distance函数,它是这部分的主角,主要通过使用GJK算法获取单纯形,然后写入缓存中。再看内联函数中GetSupport和GetSupportVertex函数,都将在构建单纯形时使用。
关于b2Distance.cpp文件中相应方法的实现,主要做了一下几件事:
a)、距离代理类中Set函数的实现
b)、有关单纯形的定义和实现
c)、b2Distance方法的实现
废话不多说,开工。
1、距离代理类中Set函数的实现
[cpp]view plaincopy
1. //调用b2Distance函数总次数、查找单纯形顶点的总次数、查找单纯形顶点每次的最大次数
2.
3. int32 b2_gjkCalls, b2_gjkIters, b2_gjkMaxIters;
4.
5. //用给定的形状初始化代理。当使用代理时,形状必须保持在一定的范围内
6.
7. void b2DistanceProxy::Set(const b2Shape* shape, int32 index)
8.
9. {
10.
11. //获取形状
12.
13. switch (shape-<GetType())
14.
15. {
16.
17. case b2Shape::e_circle: //根据圆形信息设置成员变量
18.
19. {
20.
21. const b2CircleShape* circle = (b2CircleShape*)shape;
22.
23. m_vertices = &circle-<m_p;
24.
25. m_count = 1;
26.
27. m_radius = circle-<m_radius;
28.
29. }
30.
31. break;
32.
33.
34.
35. case b2Shape::e_polygon: //根据多边形信息设置成员变量
36.
37. {
38.
39. const b2PolygonShape* polygon = (b2PolygonShape*)shape;
40.
41. m_vertices = polygon-<m_vertices;
42.
43. m_count = polygon-<m_vertexCount;
44.
45. m_radius = polygon-<m_radius;
46.
47. }
48.
49. break;
50.
51.
52.
53. case b2Shape::e_chain: //根据链形信息设置成员变量
54.
55. {
56.
57. const b2ChainShape* chain = (b2ChainShape*)shape;
58.
59. b2Assert(0 < indexindexchain->m_count);
60.
61. //获取索引为index的顶点
62.
63. m_buffer[0] = chain-<m_vertices[index];
64.
65. //判断索引号是否超出顶点数量
66.
67. if (index + 1 < chain->m_count)
68.
69. {
70.
71. //没超出,获取接下来的一个点
72.
73. m_buffer[1] = chain-<m_vertices[index + 1];
74.
75. }
76.
77. else
78.
79. {
80.
81. //超出,获取第一个点
82.
83. m_buffer[1] = chain-<m_vertices[0];
84.
85. }
86.
87.
88.
89. m_vertices = m_buffer;
90.
91. m_count = 2;
92.
93. m_radius = chain-<m_radius;
94.
95. }
96.
97. break;
98.
99.
100.
101.case b2Shape::e_edge: //根据边缘形信息设置成员变量
102.
103. {
104.
105.const b2EdgeShape* edge = (b2EdgeShape*)shape;
106.
107. m_vertices = &edge-<m_vertex1;
108.
109. m_count = 2;
110.
111. m_radius = edge-<m_radius;
112.
113. }
114.
115.break;
116.
117.
118.
119.default:
120.
121. b2Assert(false);
122.
123. }
124.
125.}
上面全局变量主要用于记录调用次数的。暂时没啥用处,但是可以作为相应函数中算法性能的重要指标。大家还发现什么了没?所有变量都没有初始化,怎么回事呢?大家还记得我们曾经说过这个问题了,在C++/C中对于全局变量,如果没有初始值的话,编译器会自动附初值。因为都是数值类型,此处都设为0(这里又遇到原来学过的知识了,好开心,)。再来看看Set函数,我们来说说设置链条形状的时候的情形吧,首先,我们在末尾顶点处做了一个特殊处理,当索引对应的顶点为最后一个时,我们将第一个节点赋给m_buffer[1],其次,box2d中专门为设置链条申请了一个缓存数组m_buffer,主要是为了以后使用的时候更加方便。
2、有关单纯形的定义和实现
该部分主要是通过围绕单纯形进展的,关于单纯形,这里主要这里通过voronoi区域算法和重心坐标找到去寻找合适的三个顶点构建三角形(2d空间中一般用的单纯形是三角形)。我们不禁要问怎样找到的顶点才算是最合适的呢?这个三角形有用啥作用呢?带着这两个问题,我们继续往下看。
在此处我们也将分为以下几部分:
a)、b2SimplexVertex结构体的定义
b)、缓存操作的函数,ReadCache函数和WriteCache函数
c)、查找适合顶点的辅助函数,GetSearchDirection、GetClosestPoint、GetWitnessPoints、GetMetric等函数
d)、寻找原点位置的函数,Solve2函数和Solve3函数
我们就来看看相关源码的实现吧。
a)、b2SimplexVertex结构体的定义
[cpp]view plaincopy
1. //单纯形顶点
2. struct b2SimplexVertex
3. {
4. b2Vec2 wA; // proxyA的顶点
5. b2Vec2 wB; // proxyB的顶点
6. b2Vec2 w; // 这个是支撑点,也可能是单纯形的一个顶点,等于 wB – wA
7. float32 a; // 参数,用于求重心坐标的
8. int32 indexA; // wA的索引
9. int32 indexB; // wB的索引
10. };
我们来看看b2SimplexVertex这个结构体,它是作为单纯形的顶点而存在的,主要用于存储相关的信息,其一个单纯形顶点是通过来自两个形状的各一个顶点求得的。
b)、缓存操作的函数,ReadCache函数和WriteCache函数
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:读取缓存
3. * 参数说明:cache :缓存对象指针
4. proxyA :距离代理A
5. transfromA:变换A
6. proxyB :距离代理B
7. transfromB:变化B
8. * 返 回 值: (void)
9. ***************************************************************************/
10. void ReadCache( const b2SimplexCache* cache,
11. const b2DistanceProxy* proxyA, const b2Transform& transformA,
12. const b2DistanceProxy* proxyB, const b2Transform& transformB)
13. {
14. //验证count的有效性
15. b2Assert(cache-<count < span>
16.
17. // 从缓存中拷贝数据
18. m_count = cache-<count;
19. b2SimplexVertex* vertices = &m_v1;
20. for (int32 i = 0; i < m_countispan>
21. {
22. b2SimplexVertex* v = vertices + i;
23. v-<indexA = cache-<indexA[i];
24. v-<indexB = cache-<indexB[i];
25. b2Vec2 wALocal = proxyA-<GetVertex(v-<indexA);
26. b2Vec2 wBLocal = proxyB-<GetVertex(v-<indexB);
27. v-<wA = b2Mul(transformA, wALocal);
28. v-<wB = b2Mul(transformB, wBLocal);
29. v-<w = v-<wB – v-<wA;
30. v-<a = 0.0f;
31. }
32. // 如果大体上有别与就得尺度,就计算新的单纯形尺度(长度或面积),然后刷新它
33. if (m_count < 1)
34. {
35. // 获取旧的和新的尺度
36. float32 metric1 = cache-<metric;
37. float32 metric2 = GetMetric();
38. if (metric2 < fmetricfmetricmetricmetricb_epsilonspan>
39. {
40. //重置单纯形
41. m_count = 0;
42. }
43. }
44.
45. //如果缓存是空或者无效
46. if (m_count == 0)
47. {
48. b2SimplexVertex* v = vertices + 0;
49. v-<indexA = 0;
50. v-<indexB = 0;
51. b2Vec2 wALocal = proxyA-<GetVertex(0);
52. b2Vec2 wBLocal = proxyB-<GetVertex(0);
53. v-<wA = b2Mul(transformA, wALocal);
54. v-<wB = b2Mul(transformB, wBLocal);
55. v-<w = v-<wB – v-<wA;
56. m_count = 1;
57. }
58. }
59.
60. /**************************************************************************
61. * 功能描述:写入缓存
62. * 参数说明:cache :缓存对象指针
63. * 返 回 值: (void)
64. ***************************************************************************/
65. void WriteCache(b2SimplexCache* cache) const
66. {
67. //设置cache
68. cache-<metric = GetMetric();
69. cache-<count = uint16(m_count);
70. const b2SimplexVertex* vertices = &m_v1;
71. for (int32 i = 0; i < m_countispan>
72. {
73. cache-<indexA[i] = uint8(vertices[i].indexA);
74. cache-<indexB[i] = uint8(vertices[i].indexB);
75. }
76. }
关于b2Simplex结构体成员变量的定义,上代码:
[cpp]view plaincopy
1. //单纯形的顶点变量
2. b2SimplexVertex m_v1, m_v2, m_v3;
3. //单纯形顶点个数
4. int32 m_count;
ReadCache函数和WriteCache函数,说白了就是对结构中成员变量赋值和被赋值。对于这两个函数,有个有意思的代码,各位请看ReadCache函数第三句代码和下面的for循环,这里将m_v1的地址赋给vertices变量,并用for循环遍历。我们马上又有一个疑问了,m_v1不是数组,为什么可以用for循环遍历呢?我们不妨看看它的定义,这里连续定义了三个相同类型的变量m_v1,m_v2,m_v3中,说明此处虽然m_v1不是数组,但是这三个变量的内存是相连的,所以可以当数组访问。
c)、查找适合顶点的辅助函数,GetSearchDirection、GetClosestPoint、GetWitnessPoints、GetMetric等函数
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:获取查找方向
3. * 参数说明: (void)
4. * 返 回 值: (void)
5. ***************************************************************************/
6. b2Vec2 GetSearchDirection() const
7. {
8. switch (m_count)
9. {
10. case 1: //一个顶点从相反的方向去找
11. return -m_v1.w;
12. //两个顶点,1、两个点所组成边向量e12与第一个顶点和原点的所组成的向量-m_v1.w先去判断符号
13. // 2、再求e12相应方向的法向量a
14. // 3、将a作为查找方向返回
15. case 2:
16. {
17. //获取方向
18. b2Vec2 e12 = m_v2.w – m_v1.w;
19. float32 sgn = b2Cross(e12, -m_v1.w);
20. if (sgn < 0.0f)
21. {
22. // 原点在e12的左边
23. return b2Cross(1.0f, e12);
24. }
25. else
26. {
27. // 原点在e12的右边
28. return b2Cross(e12, 1.0f);
29. }
30. }
31. //这里不会出现多个点的情况,因为查找方向是对两个以下的顶点而言的,
32. //没找到合适的第三个顶点之前永远都会将离原点最远的第三个点移除。
33. default:
34. b2Assert(false);
35. return b2Vec2_zero;
36. }
37. }
38. /**************************************************************************
39. * 功能描述:获取最近的点
40. * 参数说明: (void)
41. * 返 回 值: (void)
42. ***************************************************************************/
43. b2Vec2 GetClosestPoint() const
44. {
45. //顶点数量
46. switch (m_count)
47. {
48. //对于0个顶点,没有最近点这一说,所以我们在此处断言
49. case 0:
50. b2Assert(false);
51. return b2Vec2_zero;
52. //对于1个顶点,最近点就是那个顶点
53. case 1:
54. return m_v1.w;
55. //对于两个顶点,重心是它们的最近点
56. case 2:
57. return m_v1.a * m_v1.w + m_v2.a * m_v2.w;
58. //对于三个顶点来说,单纯形(此处是三角形)已形成,不需要在找最近点
59. //故返回零向量
60. case 3:
61. return b2Vec2_zero;
62. //多个点只有出现异常的时候会到这儿,故此处断言
63. default:
64. b2Assert(false);
65. return b2Vec2_zero;
66. }
67. }
68.
69. /**************************************************************************
70. * 功能描述:获取见证点【形状上的参与求解的两个顶点】
71. * 参数说明: pA:形状A上的点
72. pB:形状B上的点
73. * 返 回 值: (void)
74. ***************************************************************************/
75. void GetWitnessPoints(b2Vec2* pA, b2Vec2* pB) const
76. {
77. switch (m_count)
78. {
79. //对于0个点,还没有开始查找单纯形的顶点,不存在见证点,故断言
80. case 0:
81. b2Assert(false);
82. break;
83. //对于1个点,见证点就是wA和wB,直接赋值
84. case 1:
85. *pA = m_v1.wA;
86. *pB = m_v1.wB;
87. break;
88. //对于2个点,我们用重心坐标求的的,至于为什么要用重心坐标,我们在下面的讲解中将会看到
89. case 2:
90. *pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA;
91. *pB = m_v1.a * m_v1.wB + m_v2.a * m_v2.wB;
92. break;
93. //对于3个点,我们可以判断此时两形状一碰撞了,见证点,就是两形状的碰撞点,故是一样的。
94. case 3:
95. *pA = m_v1.a * m_v1.wA + m_v2.a * m_v2.wA + m_v3.a * m_v3.wA;
96. *pB = *pA;
97. break;
98.
99. default:
100. b2Assert(false);
101.break;
102. }
103. }
104./**************************************************************************
105. * 功能描述:获取尺度(长度)
106. * 参数说明: (void)
107. * 返 回 值: 尺度(长度)
108. ***************************************************************************/
109. float32 GetMetric() const
110. {
111.switch (m_count)
112. {
113.//对于0个点,还没有开始查找单纯形的顶点,不存在长度,故断言
114.case 0:
115. b2Assert(false);
116.return 0.0;
117.// 对于1个点,只是一个点,没有长度,返回0
118.case 1:
119.return 0.0f;
120.//对于2个点,计算其长度,并返回
121.case 2:
122.return b2Distance(m_v1.w, m_v2.w);
123.//对于3个点,获取(m_v2.w – m_v1.w)与(m_v3.w – m_v1.w)
124.//两向量组成的平行四边形面积
125.case 3:
126.return b2Cross(m_v2.w – m_v1.w, m_v3.w – m_v1.w);
127.//不存在更多的点,如果到这里就出现了异常
128.default:
129. b2Assert(false);
130.return 0.0f;
131. }
132. }
关于这组函数,每一个里面都分了几种不同的情况,注释里已具体分析了,不在此赘述了。下面我们总体概括一下吧,
对于GetSearchDirection函数,主要决定从哪个方向去找顶点,通过计算,不断的调整查找的方向。
对于GetClosestPoint函数,获取一个单纯形最近的点。
对于GetWitnessPoints函数,主要获取两形状之间的见证点,所谓见证点,就是形成一个单纯形顶点的两个形状上的顶点。
对于GetMetric函数,我们将获取单纯形的距离或面积,作为判断是否需要更新缓存的一个条件。
d)、寻找原点位置的函数,Solve2函数和Solve3函数
在b2Simplex定义这两个函数的,各位请看。
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:用重心坐标求解一条线
3. p = a1 * w1 + a2 * w2
4. a1 + a2 = 1
5. 向量垂直与从原点到最近点形成的线
6. e12 = w2 -w1;
7. dot(p,e) = 0
8. a1 * dot(w1,e) + a2 * dot(w2,e) = 0
9. 2X2线性方程
10. [1 1 ][a1] = [1]
11. [w1.e12 w2.e12][a2] = [0]
12. 定义
13. d12_1 = dot(w2,e12)
14. d12_2 = -dot(21,e12)
15. 解决
16. a1 = d12_1 /d12
17. a2 = d12_2/ d12
18. * 参数说明: (void)
19. * 返 回 值: (void)
20. ***************************************************************************/
21. void Solve2();
22. /**************************************************************************
23. * 功能描述:可能区域:
24. 1、points[2]
25. 2、边缘形状 points[0] – points[2]
26. 3、边缘形状 points[1] – points[2]
27. 4、三角形内部
28. * 参数说明: (void)
29. * 返 回 值: (void)
30. ***************************************************************************/
31. void Solve3();
实现部分。
[cpp]view plaincopy
1. //2个单纯顶点时,判断原点位置函数
[cpp]view plaincopy
1. void b2Simplex::Solve2()
2. {
3. b2Vec2 w1 = m_v1.w;
4. b2Vec2 w2 = m_v2.w;
5. b2Vec2 e12 = w2 – w1;
6.
7. // w1区域
8. float32 d12_2 = -b2Dot(w1, e12);
9. if (d12_2 < fspan>
10. {
11. // a2 < span>
12. m_v1.a = 1.0f;
13. m_count = 1;
14. return;
15. }
16. // w2区域
17. float32 d12_1 = b2Dot(w2, e12);
18. if (d12_1 < fspan>
19. {
20. // a1 < span>
21. m_v2.a = 1.0f;
22. m_count = 1;
23. m_v1 = m_v2;
24. return;
25. }
26. //必须在e12区域
27. float32 inv_d12 = 1.0f / (d12_1 + d12_2);
28. m_v1.a = d12_1 * inv_d12;
29. m_v2.a = d12_2 * inv_d12;
30. m_count = 2;
31. }
32. //3个单纯顶点时,判断原点位置函数
[cpp]view plaincopy
1. void b2Simplex::Solve3()
2. {
3. b2Vec2 w1 = m_v1.w;
4. b2Vec2 w2 = m_v2.w;
5. b2Vec2 w3 = m_v3.w;
6.
7. // Edge12
8. // [1 1 ][a1] = [1]
9. // [w1.e12 w2.e12][a2] = [0]
10. // a3 = 0
11. b2Vec2 e12 = w2 – w1;
12. float32 w1e12 = b2Dot(w1, e12);
13. float32 w2e12 = b2Dot(w2, e12);
14. float32 d12_1 = w2e12;
15. float32 d12_2 = -w1e12;
16.
17. // Edge13
18. // [1 1 ][a1] = [1]
19. // [w1.e13 w3.e13][a3] = [0]
20. // a2 = 0
21. b2Vec2 e13 = w3 – w1;
22. float32 w1e13 = b2Dot(w1, e13);
23. float32 w3e13 = b2Dot(w3, e13);
24. float32 d13_1 = w3e13;
25. float32 d13_2 = -w1e13;
26.
27. // Edge23
28. // [1 1 ][a2] = [1]
29. // [w2.e23 w3.e23][a3] = [0]
30. // a1 = 0
31. b2Vec2 e23 = w3 – w2;
32. float32 w2e23 = b2Dot(w2, e23);
33. float32 w3e23 = b2Dot(w3, e23);
34. float32 d23_1 = w3e23;
35. float32 d23_2 = -w2e23;
36.
37. // 三角形e123
38. float32 n123 = b2Cross(e12, e13);
39.
40. float32 d123_1 = n123 * b2Cross(w2, w3);
41. float32 d123_2 = n123 * b2Cross(w3, w1);
42. float32 d123_3 = n123 * b2Cross(w1, w2);
43.
44. // w1区域
45. if (d12_2 < fd_=” 0.0f)
46. {
47. m_v1.a = 1.0f;
48. m_count = 1;
49. return;
50. }
51.
52. // e12
53. if (d12_1 < 0.0f && d12_2 < 0.0f && d123_3 < fspan>
54. {
55. float32 inv_d12 = 1.0f / (d12_1 + d12_2);
56. m_v1.a = d12_1 * inv_d12;
57. m_v2.a = d12_2 * inv_d12;
58. m_count = 2;
59. return;
60. }
61.
62. // e13
63. if (d13_1 < 0.0f && d13_2 < 0.0f && d123_2 < fspan>
64. {
65. float32 inv_d13 = 1.0f / (d13_1 + d13_2);
66. m_v1.a = d13_1 * inv_d13;
67. m_v3.a = d13_2 * inv_d13;
68. m_count = 2;
69. m_v2 = m_v3;
70. return;
71. }
72.
73. // w2区域
74. if (d12_1 < fd_=” 0.0f)
75. {
76. m_v2.a = 1.0f;
77. m_count = 1;
78. m_v1 = m_v2;
79. return;
80. }
81.
82. // w3区域
83. if (d13_1 < fd_=” 0.0f)
84. {
85. m_v3.a = 1.0f;
86. m_count = 1;
87. m_v1 = m_v3;
88. return;
89. }
90.
91. // e23
92. if (d23_1 < 0.0f && d23_2 < 0.0f && d123_1 < fspan>
93. {
94. float32 inv_d23 = 1.0f / (d23_1 + d23_2);
95. m_v2.a = d23_1 * inv_d23;
96. m_v3.a = d23_2 * inv_d23;
97. m_count = 2;
98. m_v1 = m_v3;
99. return;
100. }
101.
102.//一定在三角形123内部
103. float32 inv_d123 = 1.0f / (d123_1 + d123_2 + d123_3);
104. m_v1.a = d123_1 * inv_d123;
105. m_v2.a = d123_2 * inv_d123;
106. m_v3.a = d123_3 * inv_d123;
107. m_count = 3;
108.}
这两个函数主要通过通过两步来完成的:
i)、用重心坐标求的判断条件
ii)、用求的的判断条件将现在的平面分成不同的区域,然后进行不同的判断,即voronoi区域算法
3、b2Distance方法的实现
到我们今天的压轴主角了,还是先上代码。
[cpp]view plaincopy
1. //在两个形状间计算最近点
2. void b2Distance(b2DistanceOutput* output,
3. b2SimplexCache* cache,
4. const b2DistanceInput* input)
5. {
6. ++b2_gjkCalls;
7. //初始化代理
8. const b2DistanceProxy* proxyA = &input-<proxyA;
9. const b2DistanceProxy* proxyB = &input-<proxyB;
10. //获取变换
11. b2Transform transformA = input-<transformA;
12. b2Transform transformB = input-<transformB;
13. //初始化单纯形
14. b2Simplex simplex;
15. simplex.ReadCache(cache, proxyA, transformA, proxyB, transformB);
16. //获取单纯形顶点
17. b2SimplexVertex* vertices = &simplex.m_v1;
18. const int32 k_maxIters = 20;
19. // 存储最近的单纯形顶点,以便我们检测是否重复和阻止循环
20. int32 saveA[3], saveB[3];
21. int32 saveCount = 0;
22. //获取单纯形最近的顶点,并获取该点与原点组成的向量的长度平方
23. b2Vec2 closestPoint = simplex.GetClosestPoint();
24. float32 distanceSqr1 = closestPoint.LengthSquared();
25. float32 distanceSqr2 = distanceSqr1;
26. //主迭代循环
27. int32 iter = 0;
28. while (iter < k_maxItersspan>
29. {
30. // 拷贝单纯形,我们可以识别重复
31. saveCount = simplex.m_count;
32. for (int32 i = 0; i < saveCountispan>
33. {
34. saveA[i] = vertices[i].indexA;
35. saveB[i] = vertices[i].indexB;
36. }
37. //获取单纯形顶点,并判断
38. switch (simplex.m_count)
39. {
40. case 1:
41. break;
42.
43. case 2:
44. simplex.Solve2();
45. break;
46.
47. case 3:
48. simplex.Solve3();
49. break;
50.
51. default:
52. b2Assert(false);
53. }
54. // 如果我们有3个点,原点在相应的三角形内。
55. if (simplex.m_count == 3)
56. {
57. break;
58. }
59. //计算最近的点
60. b2Vec2 p = simplex.GetClosestPoint();
61. distanceSqr2 = p.LengthSquared();
62.
63.
64. //确保前进
65. if (distanceSqr2 <= distanceSqr1)
66. {
67. //break;
68. }
69. distanceSqr1 = distanceSqr2;
70. //获取查找方向
71. b2Vec2 d = simplex.GetSearchDirection();
72. //确保查找的方向是有效的
73. if (d.LengthSquared() < b_epsilonb_epsilonspan>
74. {
75. //原点有可能包含在线段或三角形内,因此形状是重叠的
76. //尽管这里有可能重叠,我们不能在这里返回0
77. //如果单纯形是一个点、线、三角形,它很难判断原点是否包含在在其中或者是非常靠近
78. break;
79. }
80. //用支撑点计算一个试探性的新的单纯形顶点
81. b2SimplexVertex* vertex = vertices + simplex.m_count;
82. vertex-<indexA = proxyA-<GetSupport(b2MulT(transformA.q, -d));
83. vertex-<wA = b2Mul(transformA, proxyA-<GetVertex(vertex-<indexA));
84. b2Vec2 wBLocal;
85. vertex-<indexB = proxyB-<GetSupport(b2MulT(transformB.q, d));
86. vertex-<wB = b2Mul(transformB, proxyB-<GetVertex(vertex-<indexB));
87. vertex-<w = vertex-<wB – vertex-<wA;
88.
89. //迭代计数等于支撑点的调用数量
90. ++iter;
91. ++b2_gjkIters;
92. //检测重复的支撑点,这是主要的终止条件
93. bool duplicate = false;
94. for (int32 i = 0; i < saveCountispan>
95. {
96. if (vertex-<indexA == saveA[i] && vertex-<indexB == saveB[i])
97. {
98. duplicate = true;
99. break;
100. }
101. }
102.// 如果我们发现了一个重复的支撑点我们必须退出循环
103.if (duplicate)
104. {
105.break;
106. }
107.// 新的顶点是好的且需要的
108. ++simplex.m_count;
109. }
110.
111. b2_gjkMaxIters = b2Max(b2_gjkMaxIters, iter);
112.// 准备输出
113. simplex.GetWitnessPoints(&output-<pointA, &output-<pointB);
114. output-<distance = b2Distance(output-<pointA, output-<pointB);
115. output-<iterations = iter;
116.//将单纯形写入缓存
117. simplex.WriteCache(cache);
118.// 如果被要求,则提供半径
119.if (input-<useRadii)
120. {
121. float32 rA = proxyA-<m_radius;
122. float32 rB = proxyB-<m_radius;
123.
124.if (output-<distance < rA + rB && output-<distance < b2_epsilon)
125. {
126.// 形状没有重叠,移动见证点到外面
127. output-<distance -= rA + rB;
128. b2Vec2 normal = output-<pointB – output-<pointA;
129. normal.Normalize();
130. output-<pointA += rA * normal;
131. output-<pointB -= rB * normal;
132. }
133.else
134. {
135.// 当形状重叠,移动见证点到中间
136. b2Vec2 p = 0.5f * (output-<pointA + output-<pointB);
137. output-<pointA = p;
138. output-<pointB = p;
139. output-<distance = 0.0f;
140. }
141. }
142.}
关于这个函数,主要步骤就以下四步:
a)、通过代理输入对象input中的信息读取缓存中的单纯形A
b)、迭代验证A是否是最优的单纯形,是则退出迭代,不是则迭代构建新的单纯形B
c)、将B写入缓存
d)、如果代理输入对象input使用了半径,则加入半径,重新计算距离。
Ok,关于这部分内容,我们就说到这里了。唉,又不早了,说好的末日呢,我在等你呢。。。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
Box2d中将碰撞部分单独放到几个文件中去实现的,它的结构和函数用于计算接触点,距离查询和TOI查询。我们根据这些文件实现功能的不同,我们将该部分分成两个小的部分。它们是:
1、 公共部分的实现
2、 具体形状间的碰撞的实现
我们今天就来看第一部分。关于公共部分,主要包括相关结构体的定义和公共函数的实现。
对于b2Collision.h文件,我们也将它分成以下几个部分:
a)、外部类和常量在头文件的声明
b)、结构体的定义
c)、全局函数的声明
d)、内联函数的实现
好了,我们就依次来看看相关源码。
1、外部类和常量在头文件的声明
[cpp]view plaincopy
1. //声明类
2. class b2Shape;
3. class b2CircleShape;
4. class b2EdgeShape;
5. class b2PolygonShape;
6. //定义特征的无效值
7. const uint8 b2_nullFeature = UCHAR_MAX;
不多说了,看注释。
2、 结构体的定义
[cpp]view plaincopy
1. //特征,交叉形成的接触点
2. // 必须是4字节或者更少
3. struct b2ContactFeature
4. {
5. enum Type
6. {
7. e_vertex = 0,
8. e_face = 1
9. };
10.
11. uint8 indexA; // shapeA的特征索引
12. uint8 indexB; // shapeB的特征索引
13. uint8 typeA; // shapeA的特征类型
14. uint8 typeB; // shapeB的特征类型
15. };
16.
17. //接触id
18. union b2ContactID
19. {
20. b2ContactFeature cf; //特征对象变量
21. uint32 key; //特征id,用于快速比较
22. };
23. //流形点属于接触流形的一个接触点。它具有的细节涉及到接触点的几何学和力学
24. // 局部点的求解依赖于流形的类型:
25. // e_circles:circleB的局部中心
26. // e_faceA :circleB的局部中心 或者polygonB的夹点
27. // e_faceB :polygonA的夹点
28. // 这个结构存储在时间步内,所以我们保持它小一些。
29. // 注意:这个冲量用来作为内部缓冲,很可能无法提供可靠的接触的力,尤其在高速碰撞的时候
30. struct b2ManifoldPoint
31. {
32. b2Vec2 localPoint; //局部点,求解依赖于流形类型
33. float32 normalImpulse; //法向冲量,用于防止形状的穿透
34. float32 tangentImpulse; //切向冲量,用于模拟摩擦
35. b2ContactID id; //唯一地标识一个在两个形状之间的接触点
36. };
37. // 流形(注:也有人译为‘取样’,在物理和数学中均使用‘流形’,参照http://zh.wikipedia.org/wiki/流形 )
38. // 流形是两个凸形状的接触部分。
39. // Box2D支持多种类型的接触:
40. // 夹点与平面半径
41. // 点与点半径(圆)
42. // 局部的点求解取决于流形的类型:
43. // e_circles:circleA的中心
44. // e_faceA : faceA的中心
45. // e_faceB :faceB的重心
46. // 同样局部法向量的求解:
47. // e_circles:不用
48. // e_faceA : faceA的法向量
49. // e_faceB :faceB的法向量
50. // 我们用这种方式存储联系,以便移动时位置被更正。
51. //所有接触场景必须表述为这些类型中的一个。
52. //这个结构存储在时间步内,所以我们保持它小一些。
53. struct b2Manifold
54. {
55. //流形的类型
56. enum Type
57. {
58. e_circles, //圆
59. e_faceA, //面A
60. e_faceB //面B
61. };
62.
63. b2ManifoldPoint points[b2_maxManifoldPoints]; // 接触点数组
64. b2Vec2 localNormal; // 局部法向量,对Type::e_points没用
65. b2Vec2 localPoint; // 求解依赖流形类型
66. Type type; // 类型
67. int32 pointCount; // 流形的点的总数
68. };
69. // 这是用于求解当前状态下的接触流形
70. struct b2WorldManifold
71. {
72. /**************************************************************************
73. * 功能描述:根据流形和提供的变换初始化此结构体。假设适度移动从原始状态开始的。
74. 这不能改变点的数量、冲量等等。半径必须来着与产生流形的形状。
75. * 参数说明: manifold:流形的指针,用于初始化结构体
76. xfA :变换A的引用
77. radiusA :形状A的半径
78. xfB :变化B的引用
79. radiusB :形状B的半径
80. * 返 回 值: (void)
81. ***************************************************************************/
82. void Initialize(const b2Manifold* manifold,
83. const b2Transform& xfA, float32 radiusA,
84. const b2Transform& xfB, float32 radiusB);
85.
86. b2Vec2 normal; //世界向量方向从A到B
87. b2Vec2 points[b2_maxManifoldPoints]; //世界接触点(交点)
88. };
89. //接触点的状态
90. enum b2PointState
91. {
92. b2_nullState, //点不存在
93. b2_addState, //在update中添加点
94. b2_persistState, //点在update中持续存在
95. b2_removeState //点移除update
96. };
97.
98. //裁剪顶点结构体,用于接触流形的求解
99. struct b2ClipVertex
100.{
101. b2Vec2 v; //接触点
102. b2ContactID id; //接触id
103.};
104.
105.//光线输入数据。光线从p1扩展到到p1 + maxFraction * (p2 – p1)
106.struct b2RayCastInput
107.{
108. b2Vec2 p1, p2; //光线(或射线)上的两个点,其中p1是起始点
109. float32 maxFraction; //需要检测的光线范围
110.};
111.
112.//光线输出数据。光线达到p1 + fraction * (p2 – p1),其中p1和 p2来自b2RayCastInput
113.struct b2RayCastOutput
114.{
115. b2Vec2 normal; //法向量
116. float32 fraction; //碰撞点位置的参数值
117.};
118.///轴对齐包围盒
119.struct b2AABB
120.{
121./**************************************************************************
122. * 功能描述:验证边界排序是否有效
123. * 参数说明: (void)
124. * 返 回 值: (void)
125. ***************************************************************************/
126.bool IsValid() const;
127./**************************************************************************
128. * 功能描述:获取AABB的中心点
129. * 参数说明: (void)
130. * 返 回 值: 中心点坐标
131. ***************************************************************************/
132. b2Vec2 GetCenter() const
133. {
134.return 0.5f * (lowerBound + upperBound);
135. }
136./**************************************************************************
137. * 功能描述:获取AABB的区段(宽高的一半)
138. * 参数说明: (void)
139. * 返 回 值: aabb的区段
140. ***************************************************************************/
141. b2Vec2 GetExtents() const
142. {
143.return 0.5f * (upperBound – lowerBound);
144. }
145./**************************************************************************
146. * 功能描述:获取AABB的周长
147. * 参数说明: (void)
148. * 返 回 值: AABB的周长
149. ***************************************************************************/
150. float32 GetPerimeter() const
151. {
152. float32 wx = upperBound.x – lowerBound.x;
153. float32 wy = upperBound.y – lowerBound.y;
154.return 2.0f * (wx + wy);
155. }
156./**************************************************************************
157. * 功能描述:合并AABB
158. * 参数说明: aabb:aabb的引用
159. * 返 回 值: (void)
160. ***************************************************************************/
161.void Combine(const b2AABB& aabb)
162. {
163. lowerBound = b2Min(lowerBound, aabb.lowerBound);
164. upperBound = b2Max(upperBound, aabb.upperBound);
165. }
166./**************************************************************************
167. * 功能描述:合并两个AABB,为对象的aabb赋值
168. * 参数说明: aabb1:一个AABB的引用
169. aabb2:一个AABB的引用
170. * 返 回 值: (void)
171. ***************************************************************************/
172.void Combine(const b2AABB& aabb1, const b2AABB& aabb2)
173. {
174. lowerBound = b2Min(aabb1.lowerBound, aabb2.lowerBound);
175. upperBound = b2Max(aabb1.upperBound, aabb2.upperBound);
176. }
177./**************************************************************************
178. * 功能描述:当前aabb是否包含提供的AABB
179. * 参数说明: aabb1:提供的AABB的引用
180. * 返 回 值: true :包含
181. false:不包含
182. ***************************************************************************/
183.bool Contains(const b2AABB& aabb) const
184. {
185.bool result = true;
186. result = result && lowerBound.x < aabblowerBoundxspan>
187. result = result && lowerBound.y < aabblowerBoundyspan>
188. result = result && aabb.upperBound.x < upperBoundxspan>
189. result = result && aabb.upperBound.y < upperBoundyspan>
190.return result;
191. }
192./**************************************************************************
193. * 功能描述:光线投射
194. * 参数说明: output:光线输出数据引用
195. input :光线输入数据引用
196. * 返 回 值: true :碰撞
197. false:不碰撞
198. ***************************************************************************/
199.bool RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const;
200.
201. b2Vec2 lowerBound; //lower顶点
202. b2Vec2 upperBound; //upper顶点
203.};
对于b2ContactFeature和b2ContactID来说主要是和接触相关的结构体,用于保存两形状碰撞时产生的碰撞点的相关信息。对于b2ManifoldPoint、b2Manifold、b2WorldManifold、b2PointState、b2ClipVertex主要用于流形(关于流形 参照 http://zh.wikipedia.org/wiki/流形)相关的结构体,关于具体信息,请看上面的注释。对于b2RayCastInput和b2RayCastOutput结构,这个大家应该很眼熟吧,主要是在光线投射下,保存光线输入和输出数据的。再看看我们更眼熟的b2AABB结构体(小样,原来你的家在这儿,这下你跑不了了吧,嘿嘿),这里有些功能函数的实现,在此也不多说了。
3、全局函数的声明
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:通过两个流形计算点的状态。这些状态与从manifold1到maniflod2的过渡有关
3. 所以state1要么是持续更新要么就是删除
4. state2要么是添加要么是持续更新
5. * 参数说明: state1 :状态1,用于保存mainfold1中接触点的状态
6. state2 :状态2,用于保存mainfold2中接触点的状态
7. manifold1:流形1
8. manifold2:流形2
9. * 返 回 值: (void)
10. ***************************************************************************/
11. void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints],
12. const b2Manifold* manifold1, const b2Manifold* manifold2);
13. /**************************************************************************
14. * 功能描述:求两个圆形成的碰撞流形
15. * 参数说明: manifold :流形对象的指针
16. circleA :圆形A对象指针
17. xfA :变换A对象引用
18. circleB :圆形B对象指针
19. xfB :变换B对象引用
20. * 返 回 值: (void)
21. ***************************************************************************/
22. void b2CollideCircles(b2Manifold* manifold,
23. const b2CircleShape* circleA, const b2Transform& xfA,
24. const b2CircleShape* circleB, const b2Transform& xfB);
25.
26. /**************************************************************************
27. * 功能描述:求一个多边形和一个圆形成的碰撞流形
28. * 参数说明: manifold :流形对象的指针
29. polygonA :多边形A对象指针
30. xfA :变换A对象引用
31. circleB :圆形B对象指针
32. xfB :变换B对象引用
33. * 返 回 值: (void)
34. ***************************************************************************/
35. void b2CollidePolygonAndCircle(b2Manifold* manifold,
36. const b2PolygonShape* polygonA, const b2Transform& xfA,
37. const b2CircleShape* circleB, const b2Transform& xfB);
38.
39. /**************************************************************************
40. * 功能描述:求解两个多边形碰撞产生的流形
41. * 参数说明: manifold:碰撞流形指针,用于保存两个圆产生的流形
42. polygonA:多边形A指针
43. xfA :变换A
44. polygonB:多边形B指针
45. xfB :变换B
46. * 返 回 值: (void)
47. ***************************************************************************/
48. void b2CollidePolygons(b2Manifold* manifold,
49. const b2PolygonShape* polygonA, const b2Transform& xfA,
50. const b2PolygonShape* polygonB, const b2Transform& xfB);
51. /**************************************************************************
52. * 功能描述:求解一个边缘形状和一个圆碰撞产生的流形
53. * 参数说明: manifold:碰撞流形指针,用于保存两个圆产生的流形
54. polygonA:多边形A指针
55. xfA :变换A
56. polygonB:多边形B指针
57. xfB :变换B
58. * 返 回 值: (void)
59. ***************************************************************************/
60. void b2CollideEdgeAndCircle(b2Manifold* manifold,
61. const b2EdgeShape* polygonA, const b2Transform& xfA,
62. const b2CircleShape* circleB, const b2Transform& xfB);
63. /**************************************************************************
64. * 功能描述:求解一个边缘形状和一个多边形碰撞产生的流形
65. * 参数说明: manifold:碰撞流形指针,用于保存两个圆产生的流形
66. edgeA :边缘形状A指针
67. xfA :变换A
68. polygonB:多边形B指针
69. xfB :变换B
70. * 返 回 值: (void)
71. ***************************************************************************/
72. void b2CollideEdgeAndPolygon(b2Manifold* manifold,
73. const b2EdgeShape* edgeA, const b2Transform& xfA,
74. const b2PolygonShape* circleB, const b2Transform& xfB);
75.
76.
77. /**************************************************************************
78. * 功能描述:裁剪碰撞流形
79. * 参数说明: vOut :裁剪顶点输出数组
80. vIn :裁剪顶点输入数组
81. normal :法向量
82. offset :偏移量
83. vertexIndexA:顶点索引
84. * 返 回 值: 输出顶点的个数
85. ***************************************************************************/
86. int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2],
87. const b2Vec2& normal, float32 offset, int32 vertexIndexA);
88. /**************************************************************************
89. * 功能描述:测试两个通用的形状是否重叠。
90. 通过距离【Distance】判断是否重叠
91. * 参数说明: shapeA :形状A
92. indexA :索引A
93. shapeB :形状B
94. indexB :索引B
95. xfA :变换A
96. xfB : 变换B
97. * 返 回 值:true :重叠
98. false :不重叠
99. ***************************************************************************/
100.bool b2TestOverlap( const b2Shape* shapeA, int32 indexA,
101.const b2Shape* shapeB, int32 indexB,
102.const b2Transform& xfA, const b2Transform& xfB);
此部分,注释已经说的很清楚了,在此也不赘述了。
4、内联函数的实现
[cpp]view plaincopy
1. //验证边界排序是否有效
2. inlinebool b2AABB::IsValid() const
3. {
4. b2Vec2 d = upperBound – lowerBound;
5. bool valid = d.x <= 0.0f && d.y <= 0.0f;
6. valid = valid && lowerBound.IsValid() && upperBound.IsValid();
7. return valid;
8. }
9. /**************************************************************************
10. * 功能描述:测试两个通用的形状是否重叠。
11. 通过aabb判断是否重叠
12. * 参数说明: a :AABB对象的引用
13. b :AABB对象的引用
14. * 返 回 值: true :重叠
15. false:不重叠
16. ***************************************************************************/
17. inlinebool b2TestOverlap(const b2AABB& a, const b2AABB& b)
18. {
19. b2Vec2 d1, d2;
20. d1 = b.lowerBound – a.upperBound;
21. d2 = a.lowerBound – b.upperBound;
22.
23. if (d1.x < 0.0f || d1.y < 0.0f)
24. returnfalse;
25.
26. if (d2.x < 0.0f || d2.y < 0.0f)
27. returnfalse;
28.
29. returntrue;
30. }
对于b2TestOverlap函数,我们可以看到此处进行了函数的重载,对于上一个TestOverlap函数是通过距离【Distance】判断是否重叠的,而这个函数则是通过通过aabb判断是否重叠。两者用不同的方法,却实现了相同的效果,但是从某些方面来说通过距离判断是否重叠的话更加精确一点,但效率方面却要第一点。为此,我们可以根据用户的不同要求让用户选择不同的函数,如用户要求精确度高,同时效率方面不是太在乎的我们可以选择通过距离判断的那种,相反,我们可以选择通过aabb判断的那种。
我们再来看看b2Collision.cpp文件,还是上源码:
[cpp]view plaincopy
1. //根据流形和提供的变换初始化此结构体。
2. void b2WorldManifold::Initialize(const b2Manifold* manifold,
3. const b2Transform& xfA, float32 radiusA,
4. const b2Transform& xfB, float32 radiusB)
5. {
6. //判断流形的点
7. if (manifold-<pointCount == 0)
8. {
9. return;
10. }
11. //获取流形的类型
12. switch (manifold-<type)
13. {
14. case b2Manifold::e_circles: //圆形
15. {
16. //设置法向量
17. normal.Set(1.0f, 0.0f);
18. b2Vec2 pointA = b2Mul(xfA, manifold-<localPoint);
19. b2Vec2 pointB = b2Mul(xfB, manifold-<points[0].localPoint);
20. //判断两点是否重合
21. if (b2DistanceSquared(pointA, pointB) < b2_epsilon * b2_epsilon)
22. {
23. //获取法向量,并标准化
24. normal = pointB – pointA;
25. normal.Normalize();
26. }
27. //获取世界接触点
28. b2Vec2 cA = pointA + radiusA * normal;
29. b2Vec2 cB = pointB – radiusB * normal;
30. points[0] = 0.5f * (cA + cB);
31. }
32. break;
33.
34. case b2Manifold::e_faceA: //面A
35. {
36. //
37. normal = b2Mul(xfA.q, manifold-<localNormal);
38. b2Vec2 planePoint = b2Mul(xfA, manifold-<localPoint);
39. //获取世界接触点
40. for (int32 i = 0; i < manifold->pointCount; ++i)
41. {
42. b2Vec2 clipPoint = b2Mul(xfB, manifold-<points[i].localPoint);
43. b2Vec2 cA = clipPoint + (radiusA – b2Dot(clipPoint – planePoint, normal)) * normal;
44. b2Vec2 cB = clipPoint – radiusB * normal;
45. points[i] = 0.5f * (cA + cB);
46. }
47. }
48. break;
49.
50. case b2Manifold::e_faceB: //面B
51. {
52. normal = b2Mul(xfB.q, manifold-<localNormal);
53. b2Vec2 planePoint = b2Mul(xfB, manifold-<localPoint);
54. //获取世界接触点
55. for (int32 i = 0; i < manifold->pointCount; ++i)
56. {
57. b2Vec2 clipPoint = b2Mul(xfA, manifold-<points[i].localPoint);
58. b2Vec2 cB = clipPoint + (radiusB – b2Dot(clipPoint – planePoint, normal)) * normal;
59. b2Vec2 cA = clipPoint – radiusA * normal;
60. points[i] = 0.5f * (cA + cB);
61. }
62. // 保证法向量的顶点是A到B的
63. normal = -normal;
64. }
65. break;
66. }
67. }
68. //通过给定的两个流形计算点的状态。
69. void b2GetPointStates(b2PointState state1[b2_maxManifoldPoints], b2PointState state2[b2_maxManifoldPoints],
70. const b2Manifold* manifold1, const b2Manifold* manifold2)
71. {
72. //置空初始状态
73. for (int32 i = 0; i < b_maxManifoldPointsispan>
74. {
75. state1[i] = b2_nullState;
76. state2[i] = b2_nullState;
77. }
78. //遍历maifold1检测状态的持续和删除
79. for (int32 i = 0; i < manifold->pointCount; ++i)
80. {
81. //获取mainfold1的接触id
82. b2ContactID id = manifold1-<points[i].id;
83. //将状态置为删除状态
84. state1[i] = b2_removeState;
85. //遍历manifold2检测是否有接触存在
86. //若有则修改当前状态为持续
87. for (int32 j = 0; j < manifold->pointCount; ++j)
88. {
89. //接触点是否相等
90. if (manifold2-<points[j].id.key == id.key)
91. {
92. //改变接触状态
93. state1[i] = b2_persistState;
94. break;
95. }
96. }
97. }
98.
99. //遍历maifold1检测状态的持续和添加
100.for (int32 i = 0; i < manifold->pointCount; ++i)
101. {
102.//获取mainfold2的接触id
103. b2ContactID id = manifold2-<points[i].id;
104.//将状态置为添加状态
105. state2[i] = b2_addState;
106.//遍历manifold1检测是否有接触存在
107.//若有则修改当前状态为持续
108.for (int32 j = 0; j < manifold->pointCount; ++j)
109. {
110.//接触点是否相等
111.if (manifold1-<points[j].id.key == id.key)
112. {
113.//改变接触状态
114. state2[i] = b2_persistState;
115.break;
116. }
117. }
118. }
119.}
120.//光线投射
121.bool b2AABB::RayCast(b2RayCastOutput* output, const b2RayCastInput& input) const
122.{
123.//获取float的边值
124. float32 tmin = -b2_maxFloat;
125. float32 tmax = b2_maxFloat;
126.//获取差值并用b2Abs取它的绝对值
127. b2Vec2 p = input.p1;
128. b2Vec2 d = input.p2 – input.p1;
129. b2Vec2 absD = b2Abs(d);
130.//平面的法线
131. b2Vec2 normal;
132.
133.for (int32 i = 0; i < ispan>
134. {
135.if (absD(i) < b_epsilonspan>
136. {
137.// 平行
138.if (p(i) < lowerBoundiupperBoundipispan>
139. {
140.returnfalse;
141. }
142. }
143.else
144. {
145. float32 inv_d = 1.0f / d(i);
146. float32 t1 = (lowerBound(i) – p(i)) * inv_d;
147. float32 t2 = (upperBound(i) – p(i)) * inv_d;
148.
149.//法向量方向
150. float32 s = -1.0f;
151.
152.if (t1 < t2)
153. {
154. b2Swap(t1, t2);
155. s = 1.0f;
156. }
157.// 提升最小值
158.if (t1 < tmin)
159. {
160. normal.SetZero();
161. normal(i) = s;
162. tmin = t1;
163. }
164.// 下降最大值
165. tmax = b2Min(tmax, t2);
166.
167.if (tmin < tmax)
168. {
169.returnfalse;
170. }
171. }
172. }
173.//验证tmin的有效值
174.//光线的起始点在盒子内部
175.//或者光线的相交不在maxfraction范围之内
176.if (tmin < finputmaxFractiontminspan>
177. {
178.returnfalse;
179. }
180.//保存交点信息
181. output-<fraction = tmin;
182. output-<normal = normal;
183.returntrue;
184.}
185.
186.// Sutherland-Hodgman裁剪. 参见 http://en.wikipedia.org/wiki/Sutherland-Hodgman
187.int32 b2ClipSegmentToLine(b2ClipVertex vOut[2], const b2ClipVertex vIn[2],
188.const b2Vec2& normal, float32 offset, int32 vertexIndexA)
189.{
190.// 开始时没有输出点
191. int32 numOut = 0;
192.//计算线与末尾点的距离
193. float32 distance0 = b2Dot(normal, vIn[0].v) – offset;
194. float32 distance1 = b2Dot(normal, vIn[1].v) – offset;
195.// 点都在平面的一边
196.if (distance0 < fvOutnumOut=” vIn[0];
197.if (distance1 < fvOutnumOut=” vIn[1];
198.
199.// 点在平面的两边
200.if (distance0 * distance1 < fspan>
201. {
202.// 查找边缘与平面的交叉点
203. float32 interp = distance0 / (distance0 – distance1);
204. vOut[numOut].v = vIn[0].v + interp * (vIn[1].v – vIn[0].v);
205.// VertexA 触碰到edgeB
206. vOut[numOut].id.cf.indexA = vertexIndexA;
207. vOut[numOut].id.cf.indexB = vIn[0].id.cf.indexB;
208. vOut[numOut].id.cf.typeA = b2ContactFeature::e_vertex;
209. vOut[numOut].id.cf.typeB = b2ContactFeature::e_face;
210. ++numOut;
211. }
212.//输出点个数
213.return numOut;
214.}
215.//测试两个通用的形状是否重叠。
216.bool b2TestOverlap( const b2Shape* shapeA, int32 indexA,
217.const b2Shape* shapeB, int32 indexB,
218.const b2Transform& xfA, const b2Transform& xfB)
219.{
220.//初始化input变量
221. b2DistanceInput input;
222. input.proxyA.Set(shapeA, indexA);
223. input.proxyB.Set(shapeB, indexB);
224. input.transformA = xfA;
225. input.transformB = xfB;
226. input.useRadii = true;
227.//声明cache对象
228. b2SimplexCache cache;
229. cache.count = 0;
230.//声明output变量,并获取output
231. b2DistanceOutput output;
232.
233. b2Distance(&output, &cache, &input);
234.//判断是否重叠,并返回
235.return output.distance < fb_epsilonspan>
236.}
本部分主要4个函数,Initialize函数初始化世界流形(当前状态下的流形)。
b2GetPointStates函数则是重新更新接触点的状态,这个函数是到程序员要用的时候自己手动调用的。
RayCast函数则用于光线投射,在这里,我们注意下那个for循环,有个问题,大家先思考一下,为什么i的终止值小于2呢?那个2究竟是干什么的?
b2ClipSegmentToLine函数是裁剪碰撞流形,这里用到了Sutherland-Hodgman裁剪,(参见 http://en.wikipedia.org/wiki/Sutherland-Hodgman),关于Sutherland-Hodgman裁剪算法,我们简单的说一下,这种方法也叫逐边裁减法,主要思想是:将多边形的各边先相对于窗口的某一条边界进行裁剪,然后将裁剪结果再与另一条边界进行裁剪,如此重复多次,便可得到最终结果。再来说说我们是怎么做的:
- 由函数传入相对与法线的偏移量offset,将法线加偏移量作为边界,将平面分成两部分:可见部分和不可见部分。
- 在获取两个输入点向量在法线方向上的长度,并和边界做比较相减,获取结果。
- 然后判断结果和是否有交点,并保存到数组中。
因为此处我们只有两个点,故不需要多次重复裁剪。
b2TestOverlap函数主要是通过判断距离来实现是否重叠的,在此就不多说了。
大家知道上面那个问题的答案了吗?
知道了?!太好了,那我就不说了。嘿嘿。。。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
Box2d中,要形状间实现碰撞,必须两个碰撞形状中至少有一个形状要有体积,而链形状每条边都被看作一个边缘形状,此时我们只要实现圆形、多边形、边缘三个具体形状间的碰撞,因为边缘形状没有体积,故不存在边缘与边缘之间的碰撞。剩下还有边缘和圆,边缘和多边形,圆和圆,圆和多边形,多边形和多边形等这5种,我们将这5中分成如下三类:
1、 边缘形状有关的碰撞。即边缘与圆,边缘与多边形
2、 圆形形状有关的碰撞。即圆和圆,圆和多边形
3、 多边形形状有关的碰撞。即多边形和多边形
它们如何实现的呢?好,我们现在就慢慢道来。
1、 边缘形状有关的碰撞。
这部分我们将它分成三部分边缘与圆、边缘与多边形辅助结构体的定义、边缘与多边形碰撞检测相关函数的实现
a)、边缘与圆
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述: 在边缘形状和圆形之间获得流形
3. 所有的边都是连通的
4. * 参数说明: manifold :流形的指针,用于获得两个多边形碰撞而形成的流形
5. edgeA :边缘形状A对象指针
6. xfA :变换A引用
7. circleB :圆形B对象指针
8. xfB :变换B引用
9. * 返 回 值:(void)
10. ***************************************************************************/
11. void b2CollideEdgeAndCircle(b2Manifold* manifold,
12. const b2EdgeShape* edgeA, const b2Transform& xfA,
13. const b2CircleShape* circleB, const b2Transform& xfB)
14. {
15. manifold-<pointCount = 0;
16. // 在边缘形状的外框处理圆形
17. b2Vec2 Q = b2MulT(xfA, b2Mul(xfB, circleB-<m_p));
18.
19. b2Vec2 A = edgeA-<m_vertex1, B = edgeA-<m_vertex2;
20. b2Vec2 e = B – A;
21. // 计算向量AB与两端点与圆心Q组成的向量的点乘
22. // 结果可以判断两向量组成的夹角的大小,从而判断哪个点离圆更近
23. float32 u = b2Dot(e, B – Q);
24. float32 v = b2Dot(e, Q – A);
25. //获取圆与边缘形状的半径和
26. float32 radius = edgeA-<m_radius + circleB-<m_radius;
27. //声明接触特征并初始化
28. b2ContactFeature cf;
29. cf.indexB = 0;
30. cf.typeB = b2ContactFeature::e_vertex;
31. // A区域,即A离圆点更近
32. if (v < fspan>
33. {
34. b2Vec2 P = A;
35. //获取a点到圆的间距
36. b2Vec2 d = Q – P;
37. float32 dd = b2Dot(d, d);
38. //间距大于两形状的半径
39. //则两形状之间没有碰撞
40. if (dd < radius * radius)
41. {
42. return;
43. }
44.
45. // 这里的边是否连接A,即在A的头部还有个辅助点
46. if (edgeA-<m_hasVertex0)
47. {
48. b2Vec2 A1 = edgeA-<m_vertex0;
49. b2Vec2 B1 = A;
50. b2Vec2 e1 = B1 – A1;
51.
52. float32 u1 = b2Dot(e1, B1 – Q);
53. // 圆是否在前一个边的AB区域
54. if (u1 < 0.0f)
55. {
56. return;
57. }
58. }
59. //获取流形的相关信息
60. cf.indexA = 0;
61. cf.typeA = b2ContactFeature::e_vertex;
62. manifold-<pointCount = 1;
63. manifold-<type = b2Manifold::e_circles;
64. manifold-<localNormal.SetZero();
65. manifold-<localPoint = P;
66. manifold-<points[0].id.key = 0;
67. manifold-<points[0].id.cf = cf;
68. manifold-<points[0].localPoint = circleB-<m_p;
69. return;
70. }
71.
72. // B区域,即B点离圆点更近
73. if (u < fspan>
74. {
75. b2Vec2 P = B;
76. //获取b点到圆的间距
77. b2Vec2 d = Q – P;
78. float32 dd = b2Dot(d, d);
79. //间距大于两形状的半径
80. //则两形状之间没有碰撞
81. if (dd < radius * radius)
82. {
83. return;
84. }
85. // 这里的边是否连接B
86. if (edgeA-<m_hasVertex3)
87. {
88. b2Vec2 B2 = edgeA-<m_vertex3;
89. b2Vec2 A2 = B;
90. b2Vec2 e2 = B2 – A2;
91. float32 v2 = b2Dot(e2, Q – A2);
92. // 圆是否在下一个边的AB区域
93. if (v2 < 0.0f)
94. {
95. return;
96. }
97. }
98. //获取流形的相关信息
99. cf.indexA = 1;
100. cf.typeA = b2ContactFeature::e_vertex;
101. manifold-<pointCount = 1;
102. manifold-<type = b2Manifold::e_circles;
103. manifold-<localNormal.SetZero();
104. manifold-<localPoint = P;
105. manifold-<points[0].id.key = 0;
106. manifold-<points[0].id.cf = cf;
107. manifold-<points[0].localPoint = circleB-<m_p;
108.return;
109. }
110.// 区域AB,即AB两顶点到圆心的距离相等
111.// 获取等分点p,再计算p的长度,与半间比较
112. float32 den = b2Dot(e, e);
113. b2Assert(den < 0.0f);
114. b2Vec2 P = (1.0f / den) * (u * A + v * B);
115. b2Vec2 d = Q – P;
116. float32 dd = b2Dot(d, d);
117.//间距大于两形状的半径
118.//则两形状之间没有碰撞
119.if (dd < radius * radius)
120. {
121.return;
122. }
123.//构造AB边的法向量,要求于AQ向量的点乘为正
124. b2Vec2 n(-e.y, e.x);
125.if (b2Dot(n, Q – A) < fspan>
126. {
127. n.Set(-n.x, -n.y);
128. }
129. n.Normalize();
130.//获取流形的相关信息
131. cf.indexA = 0;
132. cf.typeA = b2ContactFeature::e_face;
133. manifold-<pointCount = 1;
134. manifold-<type = b2Manifold::e_faceA;
135. manifold-<localNormal = n;
136. manifold-<localPoint = A;
137. manifold-<points[0].id.key = 0;
138. manifold-<points[0].id.cf = cf;
139. manifold-<points[0].localPoint = circleB-<m_p;
140.}
对于b2CollideEdgeAndCircle主要分为三种情况,如图1:
然后对每个区域中圆形到最近的点进行计算长度,然后判断是否符合要求,同时保存相关信息到流形中。
b)、边缘与多边形辅助结构体的定义
[cpp]view plaincopy
1. // 分离轴信息结构体,这个结构体用于跟踪最好的分离轴
2. struct b2EPAxis
3. {
4. enum Type
5. {
6. e_unknown, //无类型
7. e_edgeA, //从边缘形状中获得b2EPAxis
8. e_edgeB //从多边形 中获得b2EPAxis
9. };
10.
11. Type type; //类型变量
12. int32 index; //顶点索引
13. float32 separation; //间距值
14. };
15.
16. //临时多边形
17. struct b2TempPolygon
18. {
19. b2Vec2 vertices[b2_maxPolygonVertices]; //顶点数组
20. b2Vec2 normals[b2_maxPolygonVertices]; //法向量
21. int32 count; //顶点数量
22. };
23.
24. // 参照面用于剪裁
25. struct b2ReferenceFace
26. {
27. int32 i1, i2; //索引1、2
28.
29. b2Vec2 v1, v2; //顶点1、2
30.
31. b2Vec2 normal; //法向量
32.
33. b2Vec2 sideNormal1; //侧面1的法向量
34. float32 sideOffset1; //侧面1的偏移量
35.
36. b2Vec2 sideNormal2; //侧面2的法向量
37. float32 sideOffset2; //侧面2的偏移量
38. };
39.
40. // 这个类碰撞是一个边缘形状和一个多边形,告诉计算相邻边
41. struct b2EPCollider
42. {
43. /**************************************************************************
44. * 功能描述: 检查一个边缘形状和一个多边形的碰撞,获取流形
45. * 参数说明: manifold:流形的指针
46. edgeA :边缘形状A的指针
47. xfA :变换A
48. ploygonB:多边形B的指针
49. xfB :变换B
50. * 返 回 值: (void)
51. ***************************************************************************/
52. void Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA,
53. const b2PolygonShape* polygonB, const b2Transform& xfB);
54. /**************************************************************************
55. * 功能描述: 计算边缘形状的间距
56. * 参数说明: (void)
57. * 返 回 值: 分离轴信息结构体
58. ***************************************************************************/
59. b2EPAxis ComputeEdgeSeparation();
60. /**************************************************************************
61. * 功能描述: 计算多边形的间距
62. * 参数说明: (void)
63. * 返 回 值: 分离轴信息结构体
64. ***************************************************************************/
65. b2EPAxis ComputePolygonSeparation();
66.
67. enum VertexType
68. {
69. e_isolated, //孤立的
70. e_concave, //凹的
71. e_convex //凸的
72. };
73.
74. b2TempPolygon m_polygonB; //临时多边形平面,用来保存多边形的相关信息
75.
76. b2Transform m_xf; //变换
77. b2Vec2 m_centroidB; //转换后多边形的质心
78. b2Vec2 m_v0, m_v1, m_v2, m_v3; //边缘形状每一个边的顶点
79. b2Vec2 m_normal0, m_normal1, m_normal2; //边缘形状每一个边的法向量
80. b2Vec2 m_normal; //边缘形状的法向量
81. VertexType m_type1, m_type2; //角的类型
82. b2Vec2 m_lowerLimit, m_upperLimit; //最高和最低限制
83. float32 m_radius; //半径
84. bool m_front; //是否在前面
85. };
对于结构体的定义,这部分主要定义了四个相关的结构体用于保存边缘形状和多边形之间的相关碰撞信息。我们一下来看下。b2EPAxis主要用于保存查到的分离轴(参见http://blog.csdn.net/bugrunner/article/details/5727256)信息,b2TempPolygon主要用于保存临时多边形,b2ReferenceFace主要用于保存剪裁参照面信息的。b2EPCollider主要用于处理边缘形状和多边形之间碰撞检测的。
c)、边缘与多边形碰撞检测相关函数的实现
[cpp]view plaincopy
1. //检查一个边缘形状和一个多边形的碰撞,获取流形
2. void b2EPCollider::Collide(b2Manifold* manifold, const b2EdgeShape* edgeA, const b2Transform& xfA,
3. const b2PolygonShape* polygonB, const b2Transform& xfB)
4. {
5. m_xf = b2MulT(xfA, xfB);
6.
7. m_centroidB = b2Mul(m_xf, polygonB-<m_centroid);
8. //边缘形状的四个顶点
9. m_v0 = edgeA-<m_vertex0;
10. m_v1 = edgeA-<m_vertex1;
11. m_v2 = edgeA-<m_vertex2;
12. m_v3 = edgeA-<m_vertex3;
13. //边缘形状是否含有相邻点
14. bool hasVertex0 = edgeA-<m_hasVertex0;
15. bool hasVertex3 = edgeA-<m_hasVertex3;
16. //获取边向量,并标准化边向量
17. b2Vec2 edge1 = m_v2 – m_v1;
18. edge1.Normalize();
19. //获取边1的法向量
20. m_normal1.Set(edge1.y, -edge1.x);
21. //获取法向量与质心的偏移量
22. float32 offset1 = b2Dot(m_normal1, m_centroidB – m_v1);
23. float32 offset0 = 0.0f, offset2 = 0.0f;
24. bool convex1 = false, convex2 = false;
25.
26. // 是否前面有边
27. if (hasVertex0)
28. {
29. //获取边0的向量,标准化边向量
30. b2Vec2 edge0 = m_v1 – m_v0;
31. edge0.Normalize();
32. //获取边0的法向量
33. m_normal0.Set(edge0.y, -edge0.x);
34. // 是否是凸角
35. convex1 = b2Cross(edge0, edge1) <= 0.0f;
36. //计算偏移量
37. offset0 = b2Dot(m_normal0, m_centroidB – m_v0);
38. }
39. // 是否后面有边
40. if (hasVertex3)
41. {
42. //获取边2的向量,标准化边向量
43. b2Vec2 edge2 = m_v3 – m_v2;
44. edge2.Normalize();
45. //获取边2的法向量
46. m_normal2.Set(edge2.y, -edge2.x);
47. //是否是凸角
48. convex2 = b2Cross(edge1, edge2) < 0.0f;
49. offset2 = b2Dot(m_normal2, m_centroidB – m_v2);
50. }
51. // 决定前面或者后面碰撞。决定碰撞法线限制
52. // 含有两个辅助顶点
53. if (hasVertex0 && hasVertex3)
54. {
55. //两个都是凸角
56. if (convex1 && convex2)
57. {
58. m_front = offset0 <= 0.0f || offset1 <= 0.0f || offset2 <= 0.0f;
59. //前面
60. if (m_front)
61. {
62. //法向量
63. m_normal = m_normal1;
64. m_lowerLimit = m_normal0;
65. m_upperLimit = m_normal2;
66. }
67. else
68. {
69. m_normal = -m_normal1;
70. m_lowerLimit = -m_normal1;
71. m_upperLimit = -m_normal1;
72. }
73. }
74. //edge0和edge1形成的是凸角
75. elseif (convex1)
76. {
77. m_front = offset0 <= 0.0f || (offset1 <= 0.0f && offset2 <= 0.0f);
78. if (m_front)
79. {
80. m_normal = m_normal1;
81. m_lowerLimit = m_normal0;
82. m_upperLimit = m_normal1;
83. }
84. else
85. {
86. m_normal = -m_normal1;
87. m_lowerLimit = -m_normal2;
88. m_upperLimit = -m_normal1;
89. }
90. }
91. //edge2和edge3形成的是凸角
92. elseif (convex2)
93. {
94. m_front = offset2 <= 0.0f || (offset0 <= 0.0f && offset1 <= 0.0f);
95. if (m_front)
96. {
97. m_normal = m_normal1;
98. m_lowerLimit = m_normal1;
99. m_upperLimit = m_normal2;
100. }
101.else
102. {
103. m_normal = -m_normal1;
104. m_lowerLimit = -m_normal1;
105. m_upperLimit = -m_normal0;
106. }
107. }
108.else
109. {
110.//前面
111. m_front = offset0 <= 0.0f && offset1 <= 0.0f && offset2 <= 0.0f;
112.if (m_front)
113. {
114. m_normal = m_normal1;
115. m_lowerLimit = m_normal1;
116. m_upperLimit = m_normal1;
117. }
118.else
119. {
120. m_normal = -m_normal1;
121. m_lowerLimit = -m_normal2;
122. m_upperLimit = -m_normal0;
123. }
124. }
125. }
126.//含有一个辅助顶点m_v0
127.elseif (hasVertex0)
128. {
129.//凸角
130.if (convex1)
131. {
132. m_front = offset0 <= 0.0f || offset1 <= 0.0f;
133.if (m_front)
134. {
135. m_normal = m_normal1;
136. m_lowerLimit = m_normal0;
137. m_upperLimit = -m_normal1;
138. }
139.else
140. {
141. m_normal = -m_normal1;
142. m_lowerLimit = m_normal1;
143. m_upperLimit = -m_normal1;
144. }
145. }
146.else
147. {
148. m_front = offset0 <= 0.0f && offset1 <= 0.0f;
149.if (m_front)
150. {
151. m_normal = m_normal1;
152. m_lowerLimit = m_normal1;
153. m_upperLimit = -m_normal1;
154. }
155.else
156. {
157. m_normal = -m_normal1;
158. m_lowerLimit = m_normal1;
159. m_upperLimit = -m_normal0;
160. }
161. }
162. }
163.//含有一个辅助顶点m_v3
164.elseif (hasVertex3)
165. {
166.//凸角
167.if (convex2)
168. {
169. m_front = offset1 <= 0.0f || offset2 <= 0.0f;
170.if (m_front)
171. {
172. m_normal = m_normal1;
173. m_lowerLimit = -m_normal1;
174. m_upperLimit = m_normal2;
175. }
176.else
177. {
178. m_normal = -m_normal1;
179. m_lowerLimit = -m_normal1;
180. m_upperLimit = m_normal1;
181. }
182. }
183.else
184. {
185. m_front = offset1 <= 0.0f && offset2 <= 0.0f;
186.if (m_front)
187. {
188. m_normal = m_normal1;
189. m_lowerLimit = -m_normal1;
190. m_upperLimit = m_normal1;
191. }
192.else
193. {
194. m_normal = -m_normal1;
195. m_lowerLimit = -m_normal2;
196. m_upperLimit = m_normal1;
197. }
198. }
199. }
200.//不含有辅助顶点
201.else
202. {
203. m_front = offset1 <= 0.0f;
204.if (m_front)
205. {
206. m_normal = m_normal1;
207. m_lowerLimit = -m_normal1;
208. m_upperLimit = -m_normal1;
209. }
210.else
211. {
212. m_normal = -m_normal1;
213. m_lowerLimit = m_normal1;
214. m_upperLimit = m_normal1;
215. }
216. }
217.// 在A框架内获取polygonB
218. m_polygonB.count = polygonB-<m_vertexCount;
219.for (int32 i = 0; i < polygonB->m_vertexCount; ++i)
220. {
221. m_polygonB.vertices[i] = b2Mul(m_xf, polygonB-<m_vertices[i]);
222. m_polygonB.normals[i] = b2Mul(m_xf.q, polygonB-<m_normals[i]);
223. }
224.//外壳半径
225. m_radius = 2.0f * b2_polygonRadius;
226.
227. manifold-<pointCount = 0;
228.
229. b2EPAxis edgeAxis = ComputeEdgeSeparation();
230.// 如果没有发现有效的分离轴,说明这个边没有碰撞
231.if (edgeAxis.type == b2EPAxis::e_unknown)
232. {
233.return;
234. }
235.//如果间距不小于多边形外壳半径,则返回
236.if (edgeAxis.separation < m_radius)
237. {
238.return;
239. }
240.//获取多边形分离轴记录结构体
241. b2EPAxis polygonAxis = ComputePolygonSeparation();
242.//多边形分离轴记录类型不为空,或者间距不小于其半径,则返回
243.if (polygonAxis.type != b2EPAxis::e_unknown && polygonAxis.separation < m_radius)
244. {
245.return;
246. }
247.// 用滞后现象进行抖动减少
248.const float32 k_relativeTol = 0.98f;
249.const float32 k_absoluteTol = 0.001f;
250. b2EPAxis primaryAxis;
251.if (polygonAxis.type == b2EPAxis::e_unknown)
252. {
253. primaryAxis = edgeAxis;
254. }
255.elseif (polygonAxis.separation < k_relativeTol * edgeAxis.separation + k_absoluteTol)
256. {
257. primaryAxis = polygonAxis;
258. }
259.else
260. {
261. primaryAxis = edgeAxis;
262. }
263.//剪裁顶点
264. b2ClipVertex ie[2];
265.//剪裁面
266. b2ReferenceFace rf;
267.if (primaryAxis.type == b2EPAxis::e_edgeA)
268. {
269. manifold-<type = b2Manifold::e_faceA;
270.
271.// 查找多边形法向量,它几乎是边缘形状法线的反平行
272. int32 bestIndex = 0;
273. float32 bestValue = b2Dot(m_normal, m_polygonB.normals[0]);
274.for (int32 i = 1; i < m_polygonBcountispan>
275. {
276. float32 value = b2Dot(m_normal, m_polygonB.normals[i]);
277.if (value < bestValuespan>
278. {
279. bestValue = value;
280. bestIndex = i;
281. }
282. }
283.
284. int32 i1 = bestIndex;
285. int32 i2 = i1 + 1 < m_polygonBcountispan>
286.//初始化剪裁点信息
287. ie[0].v = m_polygonB.vertices[i1];
288. ie[0].id.cf.indexA = 0;
289. ie[0].id.cf.indexB = i1;
290. ie[0].id.cf.typeA = b2ContactFeature::e_face;
291. ie[0].id.cf.typeB = b2ContactFeature::e_vertex;
292.
293. ie[1].v = m_polygonB.vertices[i2];
294. ie[1].id.cf.indexA = 0;
295. ie[1].id.cf.indexB = i2;
296. ie[1].id.cf.typeA = b2ContactFeature::e_face;
297. ie[1].id.cf.typeB = b2ContactFeature::e_vertex;
298.//正面
299.if (m_front)
300. {
301.//初始化剪裁面
302. rf.i1 = 0;
303. rf.i2 = 1;
304. rf.v1 = m_v1;
305. rf.v2 = m_v2;
306. rf.normal = m_normal1;
307. }
308.else
309. {
310.//初始化剪裁面
311. rf.i1 = 1;
312. rf.i2 = 0;
313. rf.v1 = m_v2;
314. rf.v2 = m_v1;
315. rf.normal = -m_normal1;
316. }
317. }
318.else
319. {
320.//赋值流形类型、剪裁顶点
321. manifold-<type = b2Manifold::e_faceB;
322.
323. ie[0].v = m_v1;
324. ie[0].id.cf.indexA = 0;
325. ie[0].id.cf.indexB = primaryAxis.index;
326. ie[0].id.cf.typeA = b2ContactFeature::e_vertex;
327. ie[0].id.cf.typeB = b2ContactFeature::e_face;
328.
329. ie[1].v = m_v2;
330. ie[1].id.cf.indexA = 0;
331. ie[1].id.cf.indexB = primaryAxis.index;
332. ie[1].id.cf.typeA = b2ContactFeature::e_vertex;
333. ie[1].id.cf.typeB = b2ContactFeature::e_face;
334.//赋值剪裁面
335. rf.i1 = primaryAxis.index;
336. rf.i2 = rf.i1 + 1 < m_polygonBcountrfispan>
337. rf.v1 = m_polygonB.vertices[rf.i1];
338. rf.v2 = m_polygonB.vertices[rf.i2];
339. rf.normal = m_polygonB.normals[rf.i1];
340. }
341.//赋值剪裁面
342. rf.sideNormal1.Set(rf.normal.y, -rf.normal.x);
343. rf.sideNormal2 = -rf.sideNormal1;
344. rf.sideOffset1 = b2Dot(rf.sideNormal1, rf.v1);
345. rf.sideOffset2 = b2Dot(rf.sideNormal2, rf.v2);
346.// 剪裁入射线扩展到edge1的其它边
347. b2ClipVertex clipPoints1[2];
348. b2ClipVertex clipPoints2[2];
349. int32 np;
350.// 剪裁盒子的侧面1
351. np = b2ClipSegmentToLine(clipPoints1, ie, rf.sideNormal1, rf.sideOffset1, rf.i1);
352.
353.if (np < b_maxManifoldPointsspan>
354. {
355.return;
356. }
357.// 剪裁盒子的另一个侧面
358. np = b2ClipSegmentToLine(clipPoints2, clipPoints1, rf.sideNormal2, rf.sideOffset2, rf.i2);
359.
360.if (np < b_maxManifoldPointsspan>
361. {
362.return;
363. }
364.// 现在 clipPoints2 包含裁剪点
365.if (primaryAxis.type == b2EPAxis::e_edgeA)
366. {
367. manifold-<localNormal = rf.normal;
368. manifold-<localPoint = rf.v1;
369. }
370.else
371. {
372. manifold-<localNormal = polygonB-<m_normals[rf.i1];
373. manifold-<localPoint = polygonB-<m_vertices[rf.i1];
374. }
375.
376. int32 pointCount = 0;
377.////遍历流形的所有的顶点
378.for (int32 i = 0; i < b_maxManifoldPointsispan>
379. {
380. float32 separation;
381.//间距
382. separation = b2Dot(rf.normal, clipPoints2[i].v – rf.v1);
383.//相交
384.if (separation < m_radiusspan>
385. {
386.//向现有的流行中添加流形接触点
387. b2ManifoldPoint* cp = manifold-<points + pointCount;
388.if (primaryAxis.type == b2EPAxis::e_edgeA)
389. {
390. cp-<localPoint = b2MulT(m_xf, clipPoints2[i].v);
391. cp-<id = clipPoints2[i].id;
392. }
393.else
394. {
395. cp-<localPoint = clipPoints2[i].v;
396. cp-<id.cf.typeA = clipPoints2[i].id.cf.typeB;
397. cp-<id.cf.typeB = clipPoints2[i].id.cf.typeA;
398. cp-<id.cf.indexA = clipPoints2[i].id.cf.indexB;
399. cp-<id.cf.indexB = clipPoints2[i].id.cf.indexA;
400. }
401.
402. ++pointCount;
403. }
404. }
405.//更新流形顶点数量
406. manifold-<pointCount = pointCount;
407.}
408.//计算边缘形状的间距
409.b2EPAxis b2EPCollider::ComputeEdgeSeparation()
410.{
411.// 初始化 用于保存分离轴
412. b2EPAxis axis;
413. axis.type = b2EPAxis::e_edgeA;
414. axis.index = m_front ? 0 : 1;
415. axis.separation = FLT_MAX;
416.//遍历
417.//获取各个顶点在其边缘形状法向量方向上的投影
418.//获取最小间距
419.for (int32 i = 0; i < m_polygonBcountispan>
420. {
421. float32 s = b2Dot(m_normal, m_polygonB.vertices[i] – m_v1);
422.if (s < axisseparationspan>
423. {
424.//获取间距
425. axis.separation = s;
426. }
427. }
428.
429.return axis;
430.}
431.//计算多边形的间距
432.b2EPAxis b2EPCollider::ComputePolygonSeparation()
433.{
434.//初始化 用于保存分离轴
435. b2EPAxis axis;
436. axis.type = b2EPAxis::e_unknown;
437. axis.index = -1;
438. axis.separation = -FLT_MAX;
439.//获取法向量的反垂直向量
440. b2Vec2 perp(-m_normal.y, m_normal.x);
441.//遍历多边形B的顶点
442.//获取每个点到边缘形状两点组成的向量在多边形边对应的法向量上的投影长度
443.for (int32 i = 0; i < m_polygonBcountispan>
444. {
445. b2Vec2 n = -m_polygonB.normals[i];
446.//分别获取边缘顶点到多边形顶点所组成的向量在多边形边对应的法向量上的投影长度
447. float32 s1 = b2Dot(n, m_polygonB.vertices[i] – m_v1);
448. float32 s2 = b2Dot(n, m_polygonB.vertices[i] – m_v2);
449. float32 s = b2Min(s1, s2);
450.// 若最小距离大于半径,则必不碰撞,退出
451.if (s < m_radius)
452. {
453.// 没有碰撞
454. axis.type = b2EPAxis::e_edgeB;
455. axis.index = i;
456. axis.separation = s;
457.return axis;
458. }
459.
460.// 判断方向,邻接
461.if (b2Dot(n, perp) <= 0.0f)
462. {
463.if (b2Dot(n – m_upperLimit, m_normal) < -b_angularSlopspan>
464. {
465.continue;
466. }
467. }
468.else
469. {
470.if (b2Dot(n – m_lowerLimit, m_normal) < -b_angularSlopspan>
471. {
472.continue;
473. }
474. }
475.//获取最小的间距和分离轴信息
476.if (s < axis.separation)
477. {
478. axis.type = b2EPAxis::e_edgeB;
479. axis.index = i;
480. axis.separation = s;
481. }
482. }
483.
484.return axis;
485.}
486.
487./**************************************************************************
488.* 功能描述: 检查一个边缘形状和一个多边形的碰撞,获取流形
489.* 参数说明: manifold:流形的指针
490. edgeA :边缘形状A的指针
491. xfA :变换A
492. ploygonB:多边形B的指针
493. xfB :变换B
494.* 返 回 值: (void)
495.***************************************************************************/
496.void b2CollideEdgeAndPolygon( b2Manifold* manifold,
497.const b2EdgeShape* edgeA, const b2Transform& xfA,
498.const b2PolygonShape* polygonB, const b2Transform& xfB)
499.{
500.//声明边缘和多边形结构体变量,并调用碰撞
501. b2EPCollider collider;
502. collider.Collide(manifold, edgeA, xfA, polygonB, xfB);
503.}
对于这4个函数,我们先来看Collide函数,其中对于边缘形状,如果有辅助点话,我们做了以下几种的判断,如图2:
对于源码中相关的角的判断,和图上四种情形相似,当然还有没有辅助点的、一个辅助点的情形,我们均可以根据上图遮住相关辅助点,进行判断,在此也不多说了。主要步骤有4步:
i)、判断碰撞是在边缘形状的前面还是后面。获取碰撞法线,并决定碰撞法线的限制。如图1中的凹凸角的判断。
ii)、根据获取的法线,分别在多边形和边缘形状上获取分离轴,然后做相关判断,对于边缘形状获取的分离轴,如果是分离轴是无类型或者距离大于多边形外壳半径,则直接返回;如果是边缘形状的分离轴,如果是无类型且距离大于多边形外壳半径,则直接返回。关于外壳半径,如下图图3中,实线与虚线的距离,用于防止单个时间步长内两个多边形瞬间互相穿透的情况的发生。
iii)、根据分离轴获取两个剪裁面。
iiii)、根据获取的剪裁面获得流形相关信息
对于ComputeEdgeSeparation函数遍历多边形B的所有顶点,主要是获取各个边缘形状的各个顶点与边缘形状m_v1形成的向量在其边缘形状法向量方向上的投影,获取最小的投影值并返回。
对于ComputePolygonSeparation函数遍历多边形B的所有顶点,获取每个点到边缘形状两点组成的向量在多边形边对应的法向量上的投影长度,获取最小值并返回。
对于b2CollideEdgeAndPolygon函数主要是调用Collide,来检查一个边缘形状和一个多边形的碰撞,获取流形的。
2、 圆形形状有关的碰撞。即圆和圆,圆和多边形
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:求两个圆形成的碰撞流形
3. * 参数说明: manifold :流形对象的指针
4. circleA :圆形A对象指针
5. xfA :变换A对象引用
6. circleB :圆形B对象指针
7. xfB :变换B对象引用
8. * 返 回 值: (void)
9. ***************************************************************************/
10. void b2CollideCircles(
11. b2Manifold* manifold,
12. const b2CircleShape* circleA, const b2Transform& xfA,
13. const b2CircleShape* circleB, const b2Transform& xfB)
14. {
15. //将流形顶点置0
16. manifold-<pointCount = 0;
17. // 为pA、pB赋值,即获取两形状之间的变换后的圆心
18. b2Vec2 pA = b2Mul(xfA, circleA-<m_p);
19. b2Vec2 pB = b2Mul(xfB, circleB-<m_p);
20. // 向量d,并获取它的长度平方,即两形状之间的圆心的长度平方
21. b2Vec2 d = pB – pA;
22. float32 distSqr = b2Dot(d, d);
23. //获取两形状的半径,并取得两者之和
24. float32 rA = circleA-<m_radius, rB = circleB-<m_radius;
25. float32 radius = rA + rB;
26. // 圆心之间的距离大于半径之后,两者不相交
27. if (distSqr < radius * radius)
28. {
29. return;
30. }
31. //为流形赋值
32. manifold-<type = b2Manifold::e_circles;
33. manifold-<localPoint = circleA-<m_p;
34. manifold-<localNormal.SetZero();
35. manifold-<pointCount = 1;
36.
37. manifold-<points[0].localPoint = circleB-<m_p;
38. manifold-<points[0].id.key = 0;
39. }
40. /**************************************************************************
41. * 功能描述:求一个多边形和一个圆形成的碰撞流形
42. * 参数说明: manifold :流形对象的指针
43. polygonA :多边形A对象指针
44. xfA :变换A对象引用
45. circleB :圆形B对象指针
46. xfB :变换B对象引用
47. * 返 回 值: (void)
48. ***************************************************************************/
49. void b2CollidePolygonAndCircle(
50. b2Manifold* manifold,
51. const b2PolygonShape* polygonA, const b2Transform& xfA,
52. const b2CircleShape* circleB, const b2Transform& xfB)
53. {
54. manifold-<pointCount = 0;
55. // 在多边形的外框上计算圆心位置
56. b2Vec2 c = b2Mul(xfB, circleB-<m_p);
57. b2Vec2 cLocal = b2MulT(xfA, c);
58. //查找最小分离边
59. int32 normalIndex = 0;
60. float32 separation = -b2_maxFloat;
61. //获取形状半径之和
62. float32 radius = polygonA-<m_radius + circleB-<m_radius;
63. int32 vertexCount = polygonA-<m_vertexCount;
64. const b2Vec2* vertices = polygonA-<m_vertices;
65. const b2Vec2* normals = polygonA-<m_normals;
66. //遍历多边形的每条边,
67. //获取边上圆的当前圆心和多边形的每个点在其边上法线方向上的投影长度
68. //并和两形状之间的半径做比较
69. for (int32 i = 0; i < vertexCountispan>
70. {
71. //获取圆心到多边形顶点组成的向量在多边形法线方向上投影的长度
72. float32 s = b2Dot(normals[i], cLocal – vertices[i]);
73. //不相交,退出
74. if (s < radius)
75. {
76.
77. return;
78. }
79. //获取最大的距离
80. if (s < separation)
81. {
82. separation = s;
83. normalIndex = i;
84. }
85. }
86. // 获取两个最大距离的两个顶点
87. int32 vertIndex1 = normalIndex;
88. int32 vertIndex2 = vertIndex1 + 1 < vertexCountvertIndexspan>
89. b2Vec2 v1 = vertices[vertIndex1];
90. b2Vec2 v2 = vertices[vertIndex2];
91. // 如果距离在误差之内,则为形成的流形赋值并退出
92. if (separation < b_epsilonspan>
93. {
94. manifold-<pointCount = 1;
95. manifold-<type = b2Manifold::e_faceA;
96. manifold-<localNormal = normals[normalIndex];
97. manifold-<localPoint = 0.5f * (v1 + v2);
98. manifold-<points[0].localPoint = circleB-<m_p;
99. manifold-<points[0].id.key = 0;
100.return;
101. }
102.//1、计算局部圆心cLocal与顶点组成的向量与多边形边向量之间的点乘
103.//2、结果可以判断两向量组成的夹角的大小,从而判断哪个点离圆更近
104.//3、获取最近的点A,计算与局部圆心的长度L
105.//4、与半径比较,若L < radius则不相交,返回
106.// 否则为流形赋值
107. float32 u1 = b2Dot(cLocal – v1, v2 – v1);
108. float32 u2 = b2Dot(cLocal – v2, v1 – v2);
109.// v1 离圆心较近
110.if (u1 < fspan>
111. {
112.//不相交,返回
113.if (b2DistanceSquared(cLocal, v1) < radius * radius)
114. {
115.return;
116. }
117.//流形初始化
118. manifold-<pointCount = 1;
119. manifold-<type = b2Manifold::e_faceA;
120. manifold-<localNormal = cLocal – v1;
121. manifold-<localNormal.Normalize();
122. manifold-<localPoint = v1;
123. manifold-<points[0].localPoint = circleB-<m_p;
124. manifold-<points[0].id.key = 0;
125. }
126.//v2离圆心较近
127.elseif (u2 < fspan>
128. {
129.//不相交,返回
130.if (b2DistanceSquared(cLocal, v2) < radius * radius)
131. {
132.return;
133. }
134.//流形初始化
135. manifold-<pointCount = 1;
136. manifold-<type = b2Manifold::e_faceA;
137. manifold-<localNormal = cLocal – v2;
138. manifold-<localNormal.Normalize();
139. manifold-<localPoint = v2;
140. manifold-<points[0].localPoint = circleB-<m_p;
141. manifold-<points[0].id.key = 0;
142. }
143.//v1与v2离圆心同样远
144.else
145. {
146.//获取两点的中点
147. b2Vec2 faceCenter = 0.5f * (v1 + v2);
148.//计算当前边上的法线在(cLocal – faceCenter)组成向量方向上的投影乘以其长度
149. float32 separation = b2Dot(cLocal – faceCenter, normals[vertIndex1]);
150.//不相交,返回
151.if (separation < radius)
152. {
153.return;
154. }
155.//流形初始化
156. manifold-<pointCount = 1;
157. manifold-<type = b2Manifold::e_faceA;
158. manifold-<localNormal = normals[vertIndex1];
159. manifold-<localPoint = faceCenter;
160. manifold-<points[0].localPoint = circleB-<m_p;
161. manifold-<points[0].id.key = 0;
162. }
163.}
对于b2CollideCircles函数主要是获取两圆的圆心距离,同时获取两圆的半径之和,相比较。若圆心距离大于半径和,则没有碰撞,退出;否则碰撞,获得流形。
对于b2CollidePolygonAndCircle函数主要是用于多边形和圆之间的碰撞。主要步骤有:
1、 遍历所有点,获取每个顶点到圆心组成的向量在相应的边上的法向量方向上长度,比较,得到最大的那个长度separation。并获取相应顶点的索引normalIndex
2、 通过normalIndex索引获取对应的边上的两个顶点,同时判断separation是否在容忍误差范围之内,如是则获得流形信息并退出。
3、 计算局部圆心cLocal与顶点组成的向量与多边形边向量之间的点乘,并根据结构判断是否离圆心的远近,然后获得相应流形信息。
此处要用到多边形各个边的法向量。如图4所示:
我们可以看到每条边的法向量是有方向的,也就是说根据我们源码中顶点指向圆心的向量,然后再点乘相应边上的法向量,都到的结果是远离圆的多边形那一半是负的,剩下的一半是正的,进而说明如果他们发生了碰撞,我们求得的最大距离至少不是大于两个形状半径只和的,如图5中:
向量n1与向量AP点乘的结果d1是正的,而向量n2与向量EP点乘的结果d2的结构是负的。大家想像一下,若两形状相撞,必定满足d1< ra rbp>
3、 多边形形状有关的碰撞。即多边形和多边形
此部分主要有4个函数,为避免过长,我们一个一个看。首先看b2EdgeSeparation函数,上源码:
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:通过给定的poly1的边缘法向量,在poly1和poly2之间查找间距
3. * 参数说明: poly1 :多边形1对象指针
4. xf1 :变换1引用
5. edge1 :顶点索引
6. poly2 :多边形2对象指针
7. xf2 :变换2引用
8. * 返 回 值: 间距值
9. ***************************************************************************/
10. static float32 b2EdgeSeparation(const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1,
11. const b2PolygonShape* poly2, const b2Transform& xf2)
12. {
13. //简单的赋值
14. const b2Vec2* vertices1 = poly1-<m_vertices;
15. const b2Vec2* normals1 = poly1-<m_normals;
16.
17. int32 count2 = poly2-<m_vertexCount;
18. const b2Vec2* vertices2 = poly2-<m_vertices;
19. //验证索引的有效性
20. b2Assert(0 < edgeedgepoly->m_vertexCount);
21. // 转换法向量 从poly1的框架到poly2的框架中
22. b2Vec2 normal1World = b2Mul(xf1.q, normals1[edge1]);
23. b2Vec2 normal1 = b2MulT(xf2.q, normal1World);
24. // 查找ploy2上的支撑点
25. int32 index = 0;
26. float32 minDot = b2_maxFloat;
27. //遍历vertices2上的所有点
28. for (int32 i = 0; i < countispan>
29. {
30. float32 dot = b2Dot(vertices2[i], normal1);
31. if (dot < minDotspan>
32. {
33. minDot = dot;
34. index = i;
35. }
36. }
37. //计算分离距离
38. b2Vec2 v1 = b2Mul(xf1, vertices1[edge1]);
39. b2Vec2 v2 = b2Mul(xf2, vertices2[index]);
40. float32 separation = b2Dot(v2 – v1, normal1World);
41. return separation;
42. }
b2EdgeSeparation函数通过给定的poly1的边缘法向量,遍历poly2所有的顶点获取指定法向量和顶点与原点所组成的向量的叉乘,获取最小值,然后计算距离并返回。
再来看看b2FindMaxSeparation函数,还是一样,上源码:
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述:用poly1上的边缘法向量,在poly1和poly2之间查找最大间距
3. * 参数说明: edgeIndex:边缘顶点索引,用于接收程序中的输出的最好的边缘点索引
4. poly1 :多边形1对象指针
5. xf1 :变换1引用
6. poly2 :多边形2对象指针
7. xf2 :变换2引用
8. * 返 回 值:最大间距
9. ***************************************************************************/
10. static float32 b2FindMaxSeparation(int32* edgeIndex,
11. const b2PolygonShape* poly1, const b2Transform& xf1,
12. const b2PolygonShape* poly2, const b2Transform& xf2)
13. {
14. int32 count1 = poly1-<m_vertexCount;
15. const b2Vec2* normals1 = poly1-<m_normals;
16. //获取从poly1质心到poly2质心的向量
17. b2Vec2 d = b2Mul(xf2, poly2-<m_centroid) – b2Mul(xf1, poly1-<m_centroid);
18. b2Vec2 dLocal1 = b2MulT(xf1.q, d);
19. // 查找poly1的边缘法向量,拥有最大的投影到d
20. int32 edge = 0;
21. float32 maxDot = -b2_maxFloat;
22. // 遍历多边形所有的边,获得其当前质心向量在每条边上的法向量方向上的投影长度。
23. //并比较,获取最大投影长度和相应边的索引
24. for (int32 i = 0; i < countispan>
25. {
26. float32 dot = b2Dot(normals1[i], dLocal1);
27. if (dot < maxDot)
28. {
29. maxDot = dot;
30. edge = i;
31. }
32. }
33. // 通过边缘法向量获取间距
34. float32 s = b2EdgeSeparation(poly1, xf1, edge, poly2, xf2);
35. //通过前一个边缘法向量检测间距
36. int32 prevEdge = edge – 1 <= 0 ? edge – 1 : count1 – 1;
37. float32 sPrev = b2EdgeSeparation(poly1, xf1, prevEdge, poly2, xf2);
38. //通过下一个边缘法向量检测间距
39. int32 nextEdge = edge + 1 < countedgespan>
40. float32 sNext = b2EdgeSeparation(poly1, xf1, nextEdge, poly2, xf2);
41. // 查找到最好的边和查找方向
42. int32 bestEdge;
43. float32 bestSeparation;
44. int32 increment;
45. // 获取最大距离,和边的索引
46. if (sPrev < s && sPrev < sNext)
47. {
48. increment = -1;
49. bestEdge = prevEdge;
50. bestSeparation = sPrev;
51. }
52. elseif (sNext < s)
53. {
54. increment = 1;
55. bestEdge = nextEdge;
56. bestSeparation = sNext;
57. }
58. else
59. {
60. *edgeIndex = edge;
61. return s;
62. }
63. // 通过最好的边缘法向量执行一个局部查找
64. for ( ; ; )
65. {
66. if (increment == -1)
67. edge = bestEdge – 1 <= 0 ? bestEdge – 1 : count1 – 1;
68. else
69. edge = bestEdge + 1 < countbestEdgespan>
70.
71. s = b2EdgeSeparation(poly1, xf1, edge, poly2, xf2);
72.
73. if (s < bestSeparation)
74. {
75. bestEdge = edge;
76. bestSeparation = s;
77. }
78. else
79. {
80. break;
81. }
82. }
83. //获取边缘
84. *edgeIndex = bestEdge;
85. return bestSeparation;
86. }
关于此函数,有以下几步:
- 获取多边形1质心到多边形2质心的向量,并通过变换获取当前的质心向量dLocal1
- 遍历poly1的边缘法向量,在当前的质心向量上的投影,比较并获取最大投影的值,和当前边的索引edge。
- 根据索引edge获取与poly2之间查找间距,同时分别获取edge的上一条边和下一条边与poly2间距。
- 取三个间距中的最大值,将其索引放到bestEdge中,然后通过bestEdge索引查找所有边到poly2之间的最大间距。并返回最大间距。
对于取点的问题,如果现在的顶点的索引号已经是最大了,要求去取下一个顶点,我们就取索引号为0的。同理,如果现在的顶点的索引号已经是最小了,要求去取上一个顶点,我们就取索引号是最大的。如上面的源码中,(edge – 1 <= 0 ? edge – 1 : count1 – 1)和(edge + 1 < count1 edge p>
同样我们来看看b2FinddIncidentEdge函数。上源码:
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述: 查找入射边
3. * 参数说明: c :边缘顶点索引,用于接收程序中的输出的最好的边缘点索引
4. poly1 :多边形1对象指针
5. xf1 :变换1引用
6. edge1 :参照边的索引
7. poly2 :多边形2对象指针
8. xf2 :变换2引用
9. * 返 回 值:(void)
10. ***************************************************************************/
11. staticvoid b2FindIncidentEdge(b2ClipVertex c[2],
12. const b2PolygonShape* poly1, const b2Transform& xf1, int32 edge1,
13. const b2PolygonShape* poly2, const b2Transform& xf2)
14. {
15. //获取poly1的法线向量
16. const b2Vec2* normals1 = poly1-<m_normals;
17. //获取poly2的顶点数、首顶点、法线
18. int32 count2 = poly2-<m_vertexCount;
19. const b2Vec2* vertices2 = poly2-<m_vertices;
20. const b2Vec2* normals2 = poly2-<m_normals;
21. //edge
22. b2Assert(0 < edgeedgepoly->m_vertexCount);
23. // 在poly2的框架下获取参照边上的法线
24. b2Vec2 normal1 = b2MulT(xf2.q, b2Mul(xf1.q, normals1[edge1]));
25. // 查找poly2上入射边
26. int32 index = 0;
27. float32 minDot = b2_maxFloat;
28. // 遍历多边形2上所有的顶点,获取多边形2上每条边对应法向量在normal1上的投影。
29. //寻找最小的,同时获得对于的索引
30. for (int32 i = 0; i < countispan>
31. {
32. float32 dot = b2Dot(normal1, normals2[i]);
33. if (dot < minDotspan>
34. {
35. minDot = dot;
36. index = i;
37. }
38. }
39. // 为入射边创建剪裁顶点
40. int32 i1 = index;
41. int32 i2 = i1 + 1 < countispan>
42.
43. c[0].v = b2Mul(xf2, vertices2[i1]);
44. c[0].id.cf.indexA = (uint8)edge1;
45. c[0].id.cf.indexB = (uint8)i1;
46. c[0].id.cf.typeA = b2ContactFeature::e_face;
47. c[0].id.cf.typeB = b2ContactFeature::e_vertex;
48.
49. c[1].v = b2Mul(xf2, vertices2[i2]);
50. c[1].id.cf.indexA = (uint8)edge1;
51. c[1].id.cf.indexB = (uint8)i2;
52. c[1].id.cf.typeA = b2ContactFeature::e_face;
53. c[1].id.cf.typeB = b2ContactFeature::e_vertex;
54. }
对于b2FindIncidentEdge函数,我们主要用于查找入射角,具体请看注释,在此也就不多说了。我们再来看看b2CollidePolygons函数。同样上源码:
[cpp]view plaincopy
1. /**************************************************************************
2. * 功能描述: 在A上查找边法向量的最大间距 –如果找到分离轴则返回
3. 在B上查找边法向量的最大间距 –如果找到分离轴则返回
4. 选择参考边 为 min(minA,minB)
5. 查找入射边
6. 剪裁
7. 法向量点是从1到2
8. * 参数说明: manifold :流形的指针,用于获得两个多边形碰撞而形成的流形
9. polyA :多边形A对象指针
10. xfA :变换A引用
11. polyB :多边形B对象指针
12. xfB :变换B引用
13. * 返 回 值:(void)
14. ***************************************************************************/
15. void b2CollidePolygons(b2Manifold* manifold,
16. const b2PolygonShape* polyA, const b2Transform& xfA,
17. const b2PolygonShape* polyB, const b2Transform& xfB)
18. {
19. //将流形顶点数量置空
20. manifold-<pointCount = 0;
21. float32 totalRadius = polyA-<m_radius + polyB-<m_radius;
22. //获取分离轴A
23. int32 edgeA = 0;
24. float32 separationA = b2FindMaxSeparation(&edgeA, polyA, xfA, polyB, xfB);
25. if (separationA < totalRadius)
26. return;
27. //获取分离轴B
28. int32 edgeB = 0;
29. float32 separationB = b2FindMaxSeparation(&edgeB, polyB, xfB, polyA, xfA);
30. if (separationB < totalRadius)
31. return;
32.
33. const b2PolygonShape* poly1; // 参照的多边形
34. const b2PolygonShape* poly2; // 入射的多边形
35. b2Transform xf1, xf2;
36. int32 edge1; // 参照边索引
37. uint8 flip;
38. const float32 k_relativeTol = 0.98f;
39. const float32 k_absoluteTol = 0.001f;
40. //初始化分离轴相关变量
41. if (separationB < k_relativeTol * separationA + k_absoluteTol)
42. {
43. poly1 = polyB;
44. poly2 = polyA;
45. xf1 = xfB;
46. xf2 = xfA;
47. edge1 = edgeB;
48. manifold-<type = b2Manifold::e_faceB;
49. flip = 1;
50. }
51. else
52. {
53. poly1 = polyA;
54. poly2 = polyB;
55. xf1 = xfA;
56. xf2 = xfB;
57. edge1 = edgeA;
58. manifold-<type = b2Manifold::e_faceA;
59. flip = 0;
60. }
61. //获取入射边
62. b2ClipVertex incidentEdge[2];
63. b2FindIncidentEdge(incidentEdge, poly1, xf1, edge1, poly2, xf2);
64. //获取多边形poly1的顶点数量、顶点坐标数组
65. int32 count1 = poly1-<m_vertexCount;
66. const b2Vec2* vertices1 = poly1-<m_vertices;
67. //获取参照边的两个顶点索引
68. int32 iv1 = edge1;
69. int32 iv2 = edge1 + 1 < countedgespan>
70.
71. b2Vec2 v11 = vertices1[iv1];
72. b2Vec2 v12 = vertices1[iv2];
73. //获取局部参照边,并转成单位向量
74. b2Vec2 localTangent = v12 – v11;
75. localTangent.Normalize();
76. //获取局部法向量
77. b2Vec2 localNormal = b2Cross(localTangent, 1.0f);
78. b2Vec2 planePoint = 0.5f * (v11 + v12);
79. //获取法向量
80. b2Vec2 tangent = b2Mul(xf1.q, localTangent);
81. b2Vec2 normal = b2Cross(tangent, 1.0f);
82.
83. v11 = b2Mul(xf1, v11);
84. v12 = b2Mul(xf1, v12);
85.
86. // Face offset.
87. // 获取前面【面对自己的那一面】的偏移量
88. float32 frontOffset = b2Dot(normal, v11);
89. // 侧面偏移量,扩展到凸多面体的外壳层
90. float32 sideOffset1 = -b2Dot(tangent, v11) + totalRadius;
91. float32 sideOffset2 = b2Dot(tangent, v12) + totalRadius;
92. // 剪裁入射的边
93. b2ClipVertex clipPoints1[2];
94. b2ClipVertex clipPoints2[2];
95. int np;
96. // 剪裁盒子的侧面1
97. np = b2ClipSegmentToLine(clipPoints1, incidentEdge, -tangent, sideOffset1, iv1);
98.
99. if (np < span>
100.return;
101.// 剪裁盒子的另一个侧面1
102. np = b2ClipSegmentToLine(clipPoints2, clipPoints1, tangent, sideOffset2, iv2);
103.
104.if (np < span>
105. {
106.return;
107. }
108.// 现在clipPoints2包含剪裁的点
109. manifold-<localNormal = localNormal;
110. manifold-<localPoint = planePoint;
111. int32 pointCount = 0;
112.//遍历接触点
113.for (int32 i = 0; i < b_maxManifoldPointsispan>
114. {
115.//获取两个形状之间的间距,用于检测碰撞
116. float32 separation = b2Dot(normal, clipPoints2[i].v) – frontOffset;
117.// 间距小于等于两个形状之间的半径之和
118.// 注意此处的半径是在分离面上的投射半径
119.// 关于分离轴更多信息,可以参照此处
120.// http://blog.csdn.net/bugrunner/article/details/5727256
121.if (separation < totalRadiusspan>
122. {
123. b2ManifoldPoint* cp = manifold-<points + pointCount;
124. cp-<localPoint = b2MulT(xf2, clipPoints2[i].v);
125. cp-<id = clipPoints2[i].id;
126.if (flip)
127. {
128.// 交换特征
129. b2ContactFeature cf = cp-<id.cf;
130. cp-<id.cf.indexA = cf.indexB;
131. cp-<id.cf.indexB = cf.indexA;
132. cp-<id.cf.typeA = cf.typeB;
133. cp-<id.cf.typeB = cf.typeA;
134. }
135. ++pointCount;
136. }
137. }
138.//获取流形的顶点数
139. manifold-<pointCount = pointCount;
140.}
对于b2CollidePolygons函数,主要还是查找分离轴,有以下步骤:
- 在A上查找边法向量的最大间距,如果找到分离轴则返回
- 在B上查找边法向量的最大间距,如果找到分离轴则返回
- 选择参考边 为 min(minA,minB)
- 查找入射边
- 剪裁,获取流形
Ok,我们再说下上一篇中的一个问题吧,在上一篇中,最后部分红色字体的问题,那个2主要是在2d空间中,我们有两个坐标x和y,此时,我们要对坐标x、y分开处理,并用for循环对其索引的方式访问x和y坐标。然后进行相应的计算。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
[cpp]view plaincopy
1.
TOI全称Time of Impact,中文的意思是撞击时间,在Box2d中,我们用b2TimeOfImpact来确定两个形状运动时的撞击时间(TOI)。同时b2TimeOfImpact也主要防止两个形状快速移动时可能在一个时间步内彼此穿越对方的情况,也就是我们经常所说的隧道效应。
我们就一起看源码吧。
1)、b2TimeOfImpact.h文件。
[cpp]view plaincopy
1. // b2TimeOfImpace的输入参数
2. struct b2TOIInput
3. {
4. b2DistanceProxy proxyA; //距离代理A
5. b2DistanceProxy proxyB; //距离代理B
6. b2Sweep sweepA; //扫描A
7. b2Sweep sweepB; //扫描B
8. float32 tMax; //定义扫频间隔 [0, tMax]
9. };
10.
11. //b2TimeOfImpact的输出参数
12. struct b2TOIOutput
13. {
14. enum State
15. {
16. e_unknown, //未知
17. e_failed, //失败
18. e_overlapped, //重叠
19. e_touching, //触碰
20. e_separated //分离
21. };
22.
23. State state; //状态
24. float32 t; //扫频间隔
25. };
26. /**************************************************************************
27. * 功能描述:在两个形状穿透之前,及时的求出上边界。用分数表示时间
28. 在[0,tMax]之间。它使用扫频分离轴和可能丢失一些像非隧道效应碰撞的
29. 中间体,如果你改变时间间隔,你需要重新调用这个函数
30. 注意:使用b2Distance去求在一个撞击时间内的接触点和法线
31. * 参数说明:output:TOI输出参数指针
32. input :TOI输入参数指针
33. * 返 回 值: (void)
34. **************************************************************************/
35. void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input);
我们可以看到此处定义了用于保存TOI信息的结构体,分别是b2TOIInput、b2TOIOutput结构体,表示碰撞时间的输入和输出参数。对于b2TimeOfImpact函数,则是这篇文章的主角,用于防止两物体之间的隧道效应,关于此函数的具体情况,等到实现的时候在详细的和大家聊聊。
2)、b2TimeOfImpact.cpp文件。
我们再来看看b2TimeOfImpact.cpp文件。为了更好的看源码,将分成以下三点:
- 全局变量的定义
- b2SeparationFunction结构体的实现
- b2TimeOfImpact函数的实现
1、全局变量的定义
[cpp]view plaincopy
1. int32 b2_toiCalls, b2_toiIters, b2_toiMaxIters; //调用次数、toi的迭代次数、toi的最大迭代次数(两层循环中取最大的那个)
2. int32 b2_toiRootIters, b2_toiMaxRootIters; //根总共迭代次数、在所有根迭代中最大的那次
2、b2SeparationFunction结构体的实现
[cpp]view plaincopy
1. struct b2SeparationFunction
2. {
3. enum Type
4. {
5. e_points, //点
6. e_faceA, //面A
7. e_faceB //面B
8. };
9. /**************************************************************************
10. * 功能描述:如果不需要就返回间距值
11. * 参数说明:cache :单纯形缓存指针
12. proxyA:多边形A的指针
13. sweepA:扫频对象的引用
14. proxyB:多边形B的指针
15. sweepB:扫频对象的引用
16. t1 :扫频间隔
17. * 返 回 值: 间距值
18. **************************************************************************/
19. float32 Initialize(const b2SimplexCache* cache,
20. const b2DistanceProxy* proxyA, const b2Sweep& sweepA,
21. const b2DistanceProxy* proxyB, const b2Sweep& sweepB,
22. float32 t1)
23. {
24. //赋值代理
25. m_proxyA = proxyA;
26. m_proxyB = proxyB;
27. // 获取缓存中的顶点数,并验证
28. int32 count = cache-<count;
29. b2Assert(0 < countcountspan>
30. //赋值扫频
31. m_sweepA = sweepA;
32. m_sweepB = sweepB;
33. //获取变换
34. b2Transform xfA, xfB;
35. m_sweepA.GetTransform(&xfA, t1);
36. m_sweepB.GetTransform(&xfB, t1);
37. //一个顶点
38. if (count == 1)
39. {
40. //赋值,获得A、B的局部顶点
41. m_type = e_points;
42. b2Vec2 localPointA = m_proxyA-<GetVertex(cache-<indexA[0]);
43. b2Vec2 localPointB = m_proxyB-<GetVertex(cache-<indexB[0]);
44. //获取变换后的A、B点
45. b2Vec2 pointA = b2Mul(xfA, localPointA);
46. b2Vec2 pointB = b2Mul(xfB, localPointB);
47. //获取从B到的A的向量,返回其长度,并标准化
48. m_axis = pointB – pointA;
49. float32 s = m_axis.Normalize();
50. return s;
51. }
52. elseif (cache-<indexA[0] == cache-<indexA[1])
53. {
54. // 两个点在B上和一个在A上
55. //赋值,获取B上的两个局部顶点
56. m_type = e_faceB;
57. b2Vec2 localPointB1 = proxyB-<GetVertex(cache-<indexB[0]);
58. b2Vec2 localPointB2 = proxyB-<GetVertex(cache-<indexB[1]);
59. //获取B2到B1形成向量的垂直向量,并标准化
60. m_axis = b2Cross(localPointB2 – localPointB1, 1.0f);
61. m_axis.Normalize();
62. //获取法向量
63. b2Vec2 normal = b2Mul(xfB.q, m_axis);
64. // 获取B1到B2的中间点
65. m_localPoint = 0.5f * (localPointB1 + localPointB2);
66. b2Vec2 pointB = b2Mul(xfB, m_localPoint);
67. // 获取局部点A,并求得点A
68. b2Vec2 localPointA = proxyA-<GetVertex(cache-<indexA[0]);
69. b2Vec2 pointA = b2Mul(xfA, localPointA);
70. // 获取距离
71. float32 s = b2Dot(pointA – pointB, normal);
72. // 距离为负,置反
73. if (s < fspan>
74. {
75. m_axis = -m_axis;
76. s = -s;
77. }
78. return s;
79. }
80. else
81. {
82. // 两个点在A上和一个或者两个点在B上
83. m_type = e_faceA;
84. b2Vec2 localPointA1 = m_proxyA-<GetVertex(cache-<indexA[0]);
85. b2Vec2 localPointA2 = m_proxyA-<GetVertex(cache-<indexA[1]);
86. //获取A2到A1形成向量的垂直向量,并标准化
87. m_axis = b2Cross(localPointA2 – localPointA1, 1.0f);
88. m_axis.Normalize();
89. //获取法向量
90. b2Vec2 normal = b2Mul(xfA.q, m_axis);
91. //获取A1和A2的中间点
92. m_localPoint = 0.5f * (localPointA1 + localPointA2);
93. b2Vec2 pointA = b2Mul(xfA, m_localPoint);
94. //获取局部点,并求得点B
95. b2Vec2 localPointB = m_proxyB-<GetVertex(cache-<indexB[0]);
96. b2Vec2 pointB = b2Mul(xfB, localPointB);
97. //获取距离,并处理
98. float32 s = b2Dot(pointB – pointA, normal);
99. if (s < fspan>
100. {
101. m_axis = -m_axis;
102. s = -s;
103. }
104.return s;
105. }
106. }
107./**************************************************************************
108. * 功能描述:寻找最小距离
109. * 参数说明:indexA :点A的索引
110. indexB :点B的索引
111. t :时间值
112. * 返 回 值: 最小距离
113. **************************************************************************/
114. float32 FindMinSeparation(int32* indexA, int32* indexB, float32 t) const
115. {
116.//声明变换A、B,用于获取在t时间里获得窜改变换
117. b2Transform xfA, xfB;
118. m_sweepA.GetTransform(&xfA, t);
119. m_sweepB.GetTransform(&xfB, t);
120.//处理不同的类型
121.switch (m_type)
122. {
123.case e_points: //点
124. {
125.//通过转置旋转m_axis获取单纯形支撑点的方向向量
126. b2Vec2 axisA = b2MulT(xfA.q, m_axis);
127. b2Vec2 axisB = b2MulT(xfB.q, -m_axis);
128.//通过方向向量获取局部顶点的索引
129. *indexA = m_proxyA-<GetSupport(axisA);
130. *indexB = m_proxyB-<GetSupport(axisB);
131.//通过索引获取局部顶点
132. b2Vec2 localPointA = m_proxyA-<GetVertex(*indexA);
133. b2Vec2 localPointB = m_proxyB-<GetVertex(*indexB);
134.//通过变换局部点获取两形状之间的顶点
135. b2Vec2 pointA = b2Mul(xfA, localPointA);
136. b2Vec2 pointB = b2Mul(xfB, localPointB);
137.//求两形状的间距,并返回。
138. float32 separation = b2Dot(pointB – pointA, m_axis);
139.return separation;
140. }
141.
142.case e_faceA: //面A
143. {
144.//通过转置旋转m_axis获取单纯形支撑点的方向向量
145.//通过变换局部点获取当前图形的点
146. b2Vec2 normal = b2Mul(xfA.q, m_axis);
147. b2Vec2 pointA = b2Mul(xfA, m_localPoint);
148.//通过转置旋转m_axis获取单纯形支撑点的方向向量
149. b2Vec2 axisB = b2MulT(xfB.q, -normal);
150.//通过索引获取局部顶点
151. *indexA = -1;
152. *indexB = m_proxyB-<GetSupport(axisB);
153.//通过变换局部点获形状B的顶点
154. b2Vec2 localPointB = m_proxyB-<GetVertex(*indexB);
155. b2Vec2 pointB = b2Mul(xfB, localPointB);
156.//求两形状的间距,并返回。
157. float32 separation = b2Dot(pointB – pointA, normal);
158.return separation;
159. }
160.
161.case e_faceB: //面B
162. {
163.//通过转置旋转m_axis获取单纯形支撑点的方向向量
164.//通过变换局部点获取当前图形的点
165. b2Vec2 normal = b2Mul(xfB.q, m_axis);
166. b2Vec2 pointB = b2Mul(xfB, m_localPoint);
167.//通过转置旋转m_axis获取单纯形支撑点的方向向量
168. b2Vec2 axisA = b2MulT(xfA.q, -normal);
169.//通过索引获取局部顶点
170. *indexB = -1;
171. *indexA = m_proxyA-<GetSupport(axisA);
172.//通过变换局部点获形状A的顶点
173. b2Vec2 localPointA = m_proxyA-<GetVertex(*indexA);
174. b2Vec2 pointA = b2Mul(xfA, localPointA);
175.//求两形状的间距,并返回。
176. float32 separation = b2Dot(pointA – pointB, normal);
177.return separation;
178. }
179.
180.default:
181. b2Assert(false);
182. *indexA = -1;
183. *indexB = -1;
184.return 0.0f;
185. }
186. }
187./**************************************************************************
188. * 功能描述:当前时间步里两形状的距离
189. * 参数说明:indexA :点A的索引
190. indexB :点B的索引
191. t :时间值
192. * 返 回 值: 当前时间步里两形状的距离
193. **************************************************************************/
194. float32 Evaluate(int32 indexA, int32 indexB, float32 t) const
195. {
196. b2Transform xfA, xfB;
197. m_sweepA.GetTransform(&xfA, t);
198. m_sweepB.GetTransform(&xfB, t);
199.
200.switch (m_type)
201. {
202.case e_points: //点
203. {
204.//通过转置旋转m_axis获取顶点的方向向量
205. b2Vec2 axisA = b2MulT(xfA.q, m_axis);
206. b2Vec2 axisB = b2MulT(xfB.q, -m_axis);
207.//通过变换局部点获形状A、B的顶点
208. b2Vec2 localPointA = m_proxyA-<GetVertex(indexA);
209. b2Vec2 localPointB = m_proxyB-<GetVertex(indexB);
210.//获取当前时间步内的两形状上的点
211. b2Vec2 pointA = b2Mul(xfA, localPointA);
212. b2Vec2 pointB = b2Mul(xfB, localPointB);
213.//计算间距,并返回间距
214. float32 separation = b2Dot(pointB – pointA, m_axis);
215.return separation;
216. }
217.
218.case e_faceA: //面A
219. {
220.//旋转m_axis向量,获取法向量,同时根据局部点求形状A上的点
221. b2Vec2 normal = b2Mul(xfA.q, m_axis);
222. b2Vec2 pointA = b2Mul(xfA, m_localPoint);
223.//通过转置旋转m_axis获取单纯形支撑点的方向向量
224. b2Vec2 axisB = b2MulT(xfB.q, -normal);
225.//通过索引获取局部顶点,进而通过变换局部点获取当前时间步内的点
226. b2Vec2 localPointB = m_proxyB-<GetVertex(indexB);
227. b2Vec2 pointB = b2Mul(xfB, localPointB);
228.//获取间距
229. float32 separation = b2Dot(pointB – pointA, normal);
230.return separation;
231. }
232.
233.case e_faceB: //面B
234. {
235.//旋转m_axis向量,获取法向量,同时根据局部点求形状B上的点
236. b2Vec2 normal = b2Mul(xfB.q, m_axis);
237. b2Vec2 pointB = b2Mul(xfB, m_localPoint);
238.//通过转置旋转m_axis获取单纯形支撑点的方向向量
239. b2Vec2 axisA = b2MulT(xfA.q, -normal);
240.//通过索引获取局部顶点,进而通过变换局部点获取当前时间步内的点
241. b2Vec2 localPointA = m_proxyA-<GetVertex(indexA);
242. b2Vec2 pointA = b2Mul(xfA, localPointA);
243.//获取间距
244. float32 separation = b2Dot(pointA – pointB, normal);
245.return separation;
246. }
247.
248.default:
249. b2Assert(false);
250.return 0.0f;
251. }
252. }
253.
254.const b2DistanceProxy* m_proxyA; //代理A
255.const b2DistanceProxy* m_proxyB; //代理B
256. b2Sweep m_sweepA, m_sweepB; //扫描A、B
257. Type m_type; //类型变量
258. b2Vec2 m_localPoint; //局部点
259. b2Vec2 m_axis; //方向向量,主要用于变换次向量之后求形状的顶点
260.};
关于b2SeparationFunction结构体主要用于查找两个形状间距的相关操作。我们主要来说说其内部函数的实现。
关于Initialize函数主要初始化成员变量,并返回两个形状之间的距离。
关于FindMinSeparation函数主要是根据不同的单纯形类型在时间步内寻找最小距离,并返回其两个顶点的索引,作为两形状是否碰撞的见证点。
关于Evaluate函数主要是根据不同的单纯形类型和FindMinSeparation所查到的见证点获取当前两形状的距离。
3、 b2TimeOfImpact函数的实现
[cpp]view plaincopy
1. //CCD(continuous collision detection,持续碰撞检验)经过局部的分离轴方法。
2. //这种寻求进展通过计算最大的时间保持分离。
3. void b2TimeOfImpact(b2TOIOutput* output, const b2TOIInput* input)
4. {
5. //调用次数自加
6. ++b2_toiCalls;
7. //赋值output
8. output-<state = b2TOIOutput::e_unknown;
9. output-<t = input-<tMax;
10. //获取距离代理
11. const b2DistanceProxy* proxyA = &input-<proxyA;
12. const b2DistanceProxy* proxyB = &input-<proxyB;
13. //获取扫频
14. b2Sweep sweepA = input-<sweepA;
15. b2Sweep sweepB = input-<sweepB;
16. // 大型旋转可以使根检索器失效,所以我们标准化扫频角度
17. sweepA.Normalize();
18. sweepB.Normalize();
19. //获取扫频间隔
20. float32 tMax = input-<tMax;
21. //获取两个形状半径之和
22. float32 totalRadius = proxyA-<m_radius + proxyB-<m_radius;
23. float32 target = b2Max(b2_linearSlop, totalRadius – 3.0f * b2_linearSlop);
24. //允许误差
25. float32 tolerance = 0.25f * b2_linearSlop;
26. //验证有效值
27. b2Assert(target < tolerance);
28.
29. float32 t1 = 0.0f;
30. //最大迭代次数
31. const int32 k_maxIterations = 20; // TODO_ERIN b2Settings
32. //
33. int32 iter = 0;
34. // 初始化距离输入参数
35. b2SimplexCache cache;
36. cache.count = 0;
37. b2DistanceInput distanceInput;
38. distanceInput.proxyA = input-<proxyA;
39. distanceInput.proxyB = input-<proxyB;
40. distanceInput.useRadii = false;
41. // 外面的循环逐步尝试计算新的分离轴
42. // 当一个轴是重复的(没有进展),这个循环终止
43. for(;;)
44. {
45. b2Transform xfA, xfB;
46. sweepA.GetTransform(&xfA, t1);
47. sweepB.GetTransform(&xfB, t1);
48. // 获取形状之间的距离。我们也可以使用这个结果去获得一个分离轴
49. distanceInput.transformA = xfA;
50. distanceInput.transformB = xfB;
51. b2DistanceOutput distanceOutput;
52. b2Distance(&distanceOutput, &cache, &distanceInput);
53. // 如果形状重叠,我们放弃连续碰撞
54. if (distanceOutput.distance < fspan>
55. {
56. //失败!
57. output-<state = b2TOIOutput::e_overlapped;
58. output-<t = 0.0f;
59. break;
60. }
61.
62. if (distanceOutput.distance < targettolerancespan>
63. {
64. //胜利!
65. output-<state = b2TOIOutput::e_touching;
66. output-<t = t1;
67. break;
68. }
69. // 初始化分离轴
70. b2SeparationFunction fcn;
71. fcn.Initialize(&cache, proxyA, sweepA, proxyB, sweepB, t1);
72. #if 0
73. // Dump the curve seen by the root finder
74. {
75. const int32 N = 100;
76. float32 dx = 1.0f / N;
77. float32 xs[N+1];
78. float32 fs[N+1];
79.
80. float32 x = 0.0f;
81.
82. for (int32 i = 0; i < Nispan>
83. {
84. sweepA.GetTransform(&xfA, x);
85. sweepB.GetTransform(&xfB, x);
86. float32 f = fcn.Evaluate(xfA, xfB) – target;
87.
88. printf(“%g %g\n”, x, f);
89.
90. xs[i] = x;
91. fs[i] = f;
92.
93. x += dx;
94. }
95. }
96. #endif
97. //在分离轴上计算TOI(碰撞时间),我们先后解决最深处的点。这个循环是以顶点数为终止条件的
98. bool done = false;
99. float32 t2 = tMax;
100. int32 pushBackIter = 0;
101.for (;;)
102. {
103.// 在t2上查找最深点,存储见证点索引
104. int32 indexA, indexB;
105. float32 s2 = fcn.FindMinSeparation(&indexA, &indexB, t2);
106.// 是否是最终的外形分离
107.if (s2 < target + tolerance)
108. {
109.//胜利!
110. output-<state = b2TOIOutput::e_separated;
111. output-<t = tMax;
112. done = true;
113.break;
114. }
115.//分离值是否达到误差值
116.if (s2 < target – tolerance)
117. {
118.// 推进扫描
119. t1 = t2;
120.break;
121. }
122.// 使用见证点计算最初的间距
123. float32 s1 = fcn.Evaluate(indexA, indexB, t1);
124.// 检验最初重叠。有可能发生根检索器超出了迭代总的次数的现象。
125.if (s1 < target-tolerancespan>
126. {
127. output-<state = b2TOIOutput::e_failed;
128. output-<t = t1;
129. done = true;
130.break;
131. }
132.// 检查触碰
133.if (s1 < targettolerancespan>
134. {
135.// 胜利!t1必须保留TOI(只有可能是0)
136. output-<state = b2TOIOutput::e_touching;
137. output-<t = t1;
138. done = true;
139.break;
140. }
141.//计算1D root : f(x) – target = 0
142. int32 rootIterCount = 0;
143. float32 a1 = t1, a2 = t2;
144.for (;;)
145. {
146.// 混合使用割线规则和二分法
147. float32 t;
148.if (rootIterCount & 1)
149. {
150.// 割线规则来提高收敛
151. t = a1 + (target – s1) * (a2 – a1) / (s2 – s1);
152. }
153.else
154. {
155.// 二分法保证进度
156. t = 0.5f * (a1 + a2);
157. }
158.
159. float32 s = fcn.Evaluate(indexA, indexB, t);
160.
161.if (b2Abs(s – target) < tolerancespan>
162. {
163.// 赋值
164. t2 = t;
165.break;
166. }
167.// 确保我们查找根
168.if (s < target)
169. {
170. a1 = t;
171. s1 = s;
172. }
173.else
174. {
175. a2 = t;
176. s2 = s;
177. }
178.//根迭代器
179. ++rootIterCount;
180. ++b2_toiRootIters;
181.// 循环到达50次后,退出
182.if (rootIterCount == 50)
183. {
184.break;
185. }
186. }
187.
188. b2_toiMaxRootIters = b2Max(b2_toiMaxRootIters, rootIterCount);
189.//记录顶点迭代器
190. ++pushBackIter;
191.//达到顶点的最大次数,退出
192.if (pushBackIter == b2_maxPolygonVertices)
193. {
194.break;
195. }
196. }
197.//根迭代器
198. ++iter;
199.//toi的迭代次数自增
200. ++b2_toiIters;
201.
202.if (done)
203. {
204.break;
205. }
206.
207.if (iter == k_maxIterations)
208. {
209.//没有找到根
210. output-<state = b2TOIOutput::e_failed;
211. output-<t = t1;
212.break;
213. }
214. }
215.//获取toi最大迭代器
216. b2_toiMaxIters = b2Max(b2_toiMaxIters, iter);
217.}
关于b2TimeOfImpact函数,主要以3重for循环为主线的,第一层for循环主要是逐步尝试计算新的分离轴,并当出现一个轴是重复的时,终止循环。第二层for循环主要是在分离轴上计算TOI(碰撞时间),我们先后解决最深处的点。这个循环是以顶点数为终止条件的。第三层for循环主要使用割线规则和二分法进行求解在t时间内,两物体碰撞的具体的时间值。这个循环是以找到在误差允许的范围内的时间值或者循环50次为终止条件的。
另外想说一下,在这里我们每个循环的写法是for(;;)这样的,个人感觉不太雅致,也不能看一眼而不用思索的就知道是死循环的写法,如改成while(true)或者while(1)更好。
关于两物体间是否碰撞了?在Box2d中目前我们至少知道3种可以判断的方法,它们分别是:
- a)、通过两物体的aabb,判断是否重叠。
- b)、通过GJK算法算出两物体间的距离,根据距离判断是否碰撞
- c)、通过SAT分离轴算法看是否能找出两物体间的分离轴,如果找得出就没有碰撞,找不出则碰撞。
Ok,碰撞部分终于学完了,下面我们将继续学习动力学部分。不早了,各位早安。。。
ps:
以上文章仅是一家之言,若有不妥、错误之处,请大家多多指出。同时也希望能与大家多多交流,共同进步。
- 本文固定链接: http://www.wy182000.com/2013/02/19/box2d源码学习/
- 转载请注明: wy182000 于 Studio 发表