KDnode.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. /*
  2. ** Copyright (C) 1996, 1997 Microsoft Corporation. All Rights Reserved.
  3. **
  4. ** File: KDnode.cpp
  5. **
  6. ** Author:
  7. **
  8. ** Description:
  9. **
  10. ** History:
  11. */
  12. #include "pch.h"
  13. //Allocate space for the node's hitTest and endpoint arrays
  14. //(as a single allocation)
  15. void KDnode::allocHitTestsEndpoints(int n)
  16. {
  17. if (m_maxHitTests < n)
  18. {
  19. //Not enough space ... realloc
  20. delete [] m_ppHitTests;
  21. void** v = new void* [n * (1 + 2 * c_nAxes)];
  22. assert (v);
  23. m_maxHitTests = n;
  24. m_ppHitTests = (HitTest**)v;
  25. m_ppEndpoints[0] = (Endpoint**)(v + n);
  26. for (int i = 1; (i < c_nAxes); i++)
  27. m_ppEndpoints[i] = m_ppEndpoints[i - 1] + (n << 1);
  28. }
  29. }
  30. KDnode::KDnode(KDnode* parent)
  31. :
  32. m_parent(parent),
  33. m_pivotAxis(-1),
  34. m_pivotValue(0.0f),
  35. m_nHitTests(0),
  36. m_maxHitTests(0),
  37. m_ppHitTests(NULL)
  38. {
  39. m_ppEndpoints[0] = NULL;
  40. m_children[0] = m_children[1] = NULL;
  41. }
  42. KDnode::~KDnode(void)
  43. {
  44. delete m_children[1];
  45. delete m_children[0];
  46. delete [] m_ppHitTests;
  47. }
  48. void KDnode::reset(bool highF)
  49. {
  50. assert (m_parent);
  51. //Set asside enough space for the same number of hitTests as the parent
  52. //(pessimistic, but fast)
  53. allocHitTestsEndpoints(m_parent->m_nHitTests);
  54. int pivotAxis = m_parent->m_pivotAxis;
  55. assert (pivotAxis >= 0);
  56. assert (pivotAxis < c_nAxes);
  57. float pivotValue = m_parent->m_pivotValue;
  58. //Extract and mark the subset of the parent's hitTests that belong to this node
  59. {
  60. m_nHitTests = 0;
  61. for (HitTest** ppHitTest = &m_parent->m_ppHitTests[m_parent->m_nHitTests - 1];
  62. (ppHitTest >= m_parent->m_ppHitTests);
  63. ppHitTest--)
  64. {
  65. HitTest* pHitTest = *ppHitTest;
  66. assert (pHitTest);
  67. if (highF)
  68. {
  69. //Creating the high child ... anything extending above the pivot value is accepted
  70. if (pHitTest->m_boundingBox.axes[pivotAxis].values[1] > pivotValue)
  71. {
  72. pHitTest->m_activeF = true;
  73. m_ppHitTests[m_nHitTests++] = pHitTest;
  74. }
  75. else
  76. pHitTest->m_activeF = false;
  77. }
  78. else
  79. {
  80. //Creating the low child ... anything extending below the pivot value is accepted
  81. if (pHitTest->m_boundingBox.axes[pivotAxis].values[0] < pivotValue)
  82. {
  83. pHitTest->m_activeF = true;
  84. m_ppHitTests[m_nHitTests++] = pHitTest;
  85. }
  86. else
  87. pHitTest->m_activeF = false;
  88. }
  89. }
  90. assert (m_nHitTests != 0);
  91. assert (m_nHitTests != m_parent->m_nHitTests);
  92. }
  93. if (m_nHitTests >= c_minNodeSize)
  94. {
  95. //Generate the sorted endpoint lists for this node
  96. //use the fact that the parent's endpoint lists are sorted
  97. //and that the endpoint lists for this node will be a subset
  98. //(consisting of only the endpoints for the "active" hitTests)
  99. for (int axis = 0; (axis < c_nAxes); axis++)
  100. {
  101. //Work backwards to so the conditionals test vs. a non-equation.
  102. int thisID = (m_nHitTests << 1) - 1;
  103. //Note: the condition tests thisID so that the loop will stop as soon as
  104. //the endpoint list is filled (the remaining endpoints in the parent's
  105. //endpoint list must not be active).
  106. for (int parentID = (m_parent->m_nHitTests << 1) - 1;
  107. (thisID >= 0);
  108. parentID--)
  109. {
  110. assert (parentID >= 0);
  111. HitTest* pHitTest = m_parent->m_ppEndpoints[axis][parentID]->pHitTest;
  112. if (pHitTest->m_activeF)
  113. m_ppEndpoints[axis][thisID--] = m_parent->m_ppEndpoints[axis][parentID];
  114. }
  115. //There's no need to sort the endpoint list (since is was sorted in the parent).
  116. }
  117. pivot();
  118. }
  119. else
  120. m_pivotAxis = -1;
  121. }
  122. static bool findPivot(int nHitTests,
  123. Endpoint* const * ppEndpoints,
  124. float* pValue,
  125. int* pCost)
  126. {
  127. assert (nHitTests >= c_minNodeSize);
  128. assert (ppEndpoints);
  129. assert (pValue);
  130. assert (pCost);
  131. bool optimalF = false;
  132. int nEndpoints = nHitTests << 1;
  133. //costBias is used to guarantee that the pivot is worthwhile. Must be
  134. //in the range [nHitTests, nHitTests * 2].
  135. int costBias = (5 * nHitTests) >> 2;
  136. //This is used to generate the minCost (below) and test for the when it's time to quit
  137. int bestBaseCost = costBias - (nHitTests << 1);
  138. //Theoretical lowest cost for this node ... account for the fact that it is impossible to
  139. //perfectly balance a node with an odd number of hitTests
  140. int minCost = bestBaseCost + (nHitTests & 0x01); //fast mod 2
  141. //The endpoints start with a low and end with a high, meaning we can skip the first endpoint
  142. //in the loop (hence the pre-increment) and only test for exit on the high endpoints.
  143. assert (!ppEndpoints[0]->highF);
  144. assert (ppEndpoints[(nHitTests << 1) - 1]->highF);
  145. int nLow = 0; //Number of intervals completely below of the current endpoint
  146. int nHigh = nHitTests - 1; //Number of intervales completely above the current endpoint
  147. //Only pivots with a cost less than zero will be considered by pivot, so set the best cost to
  148. //zero so the only the positives costs pivots will be ignored.
  149. float bestValue;
  150. int bestCost = 0;
  151. while (true)
  152. {
  153. assert (nLow < nHitTests);
  154. const Endpoint* pEndpoint = *(++ppEndpoints);
  155. if (pEndpoint->highF)
  156. {
  157. //crossed a high endpoint, meaning there is one less
  158. //interval completely below the current point
  159. nLow++;
  160. //Calculate the unbalanced and totals here since this will give a lower
  161. //overall cost than calculating it before nLow is incremented.
  162. int unbalanced = nLow - nHigh;
  163. int baseCost = costBias - ((nLow + nHigh) << 1);
  164. //thisCost == base + |unbalanced| ... just do it fast
  165. int thisCost = (unbalanced >= 0)
  166. ? (baseCost + unbalanced)
  167. : (baseCost - unbalanced);
  168. if (thisCost < bestCost)
  169. {
  170. bestCost = thisCost;
  171. bestValue = pEndpoint->value;
  172. if (bestCost == minCost)
  173. {
  174. optimalF = true;
  175. break;
  176. }
  177. }
  178. else if ((bestCost <= bestBaseCost + unbalanced) ||
  179. (nLow == nHitTests))
  180. {
  181. //Since unbalanced only increases, we can give up when the best cost
  182. //is <= the best cost we can hope to achieve
  183. //we can also give up if we've reached the end of the endpoint list
  184. break;
  185. }
  186. }
  187. else
  188. {
  189. //No need to calculate costs ... either the previous endpoint was a right
  190. //endpoint (and the cost wouldn't change since nHigh would be decremented
  191. //after caclulating the cost) or it was a left endpoint (in which case the
  192. //cost could only be higher than it was before).
  193. //Crossed a low endpoint, meaning there is one less
  194. //interval completely above the current point
  195. nHigh--;
  196. }
  197. }
  198. *pCost = bestCost;
  199. *pValue = bestValue;
  200. return optimalF;
  201. }
  202. void KDnode::pivot(void)
  203. {
  204. //Select the best pivot axis and value for this node
  205. //based on the endpoint data
  206. assert (m_nHitTests >= c_minNodeSize);
  207. //Rotate the axes to encourage (in the event of a tie for best cost)
  208. //that children pivot on different axes than their parents.
  209. int startAxis = m_parent ? ((m_parent->m_pivotAxis + 1) % c_nAxes) : 0;
  210. int bestAxis = startAxis;
  211. float bestValue;
  212. int bestCost;
  213. bool optimalF = findPivot(m_nHitTests,
  214. m_ppEndpoints[bestAxis],
  215. &bestValue,
  216. &bestCost);
  217. for (int i = 1; ((!optimalF) && (i < c_nAxes)); i++)
  218. {
  219. int axis = (i + startAxis) % c_nAxes;
  220. float value;
  221. int cost;
  222. optimalF = findPivot(m_nHitTests,
  223. m_ppEndpoints[axis],
  224. &value, &cost);
  225. if (cost < bestCost)
  226. {
  227. bestAxis = axis;
  228. bestValue = value;
  229. bestCost = cost;
  230. }
  231. }
  232. if (bestCost < 0)
  233. {
  234. //Found a pivot value that did some good ... repivot on the new pivot point
  235. m_pivotAxis = bestAxis;
  236. m_pivotValue = bestValue;
  237. if (!m_children[0])
  238. {
  239. assert (!m_children[1]);
  240. //This node has no children ... create some
  241. m_children[0] = new KDnode(this);
  242. m_children[1] = new KDnode(this);
  243. }
  244. assert (m_children[0] && m_children[1]);
  245. //Reset both children based on the new pivot axis/value.
  246. m_children[0]->reset(false);
  247. m_children[1]->reset(true);
  248. }
  249. else
  250. m_pivotAxis = -1;
  251. }
  252. void KDnode::test(HitTest* pHitTest1,
  253. CollisionQueue* pQueue) const
  254. {
  255. if (m_pivotAxis >= 0)
  256. {
  257. //Pass the hit tests along to one or both children
  258. const Interval& interval = pHitTest1->m_boundingBox.axes[m_pivotAxis];
  259. if (interval.values[0] < m_pivotValue)
  260. m_children[0]->test(pHitTest1, pQueue);
  261. if (interval.values[1] > m_pivotValue)
  262. m_children[1]->test(pHitTest1, pQueue);
  263. }
  264. else
  265. {
  266. //this is a terminal node: test bb against each contained hitTest's bounding box
  267. for (HitTest** ppHitTest = &m_ppHitTests[m_nHitTests - 1];
  268. (ppHitTest >= m_ppHitTests);
  269. ppHitTest--)
  270. {
  271. HitTest* pHitTest2 = *ppHitTest;
  272. if ((pHitTest1->m_id < pHitTest2->m_id) &&
  273. (pHitTest2->m_lastTest != pHitTest1) &&
  274. (pHitTest1->m_noHit != pHitTest2) &&
  275. (pHitTest2->m_noHit != pHitTest1))
  276. {
  277. pHitTest2->m_lastTest = pHitTest1;
  278. pHitTest1->Collide(pHitTest2, pQueue);
  279. }
  280. }
  281. }
  282. }