bvh_cull.inc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. public:
  2. // cull parameters is a convenient way of passing a bunch
  3. // of arguments through the culling functions without
  4. // writing loads of code. Not all members are used for some cull checks
  5. struct CullParams {
  6. int result_count_overall; // both trees
  7. int result_count; // this tree only
  8. int result_max;
  9. T **result_array;
  10. int *subindex_array;
  11. // We now process masks etc in a user template function,
  12. // and these for simplicity assume even for cull tests there is a
  13. // testing object (which has masks etc) for the user cull checks.
  14. // This means for cull tests on their own, the client will usually
  15. // want to create a dummy object, just in order to specify masks etc.
  16. const T *tester;
  17. // optional components for different tests
  18. POINT point;
  19. BVHABB_CLASS abb;
  20. typename BVHABB_CLASS::ConvexHull hull;
  21. typename BVHABB_CLASS::Segment segment;
  22. // When collision testing, we can specify which tree ids
  23. // to collide test against with the tree_collision_mask.
  24. uint32_t tree_collision_mask;
  25. };
  26. private:
  27. void _cull_translate_hits(CullParams &p) {
  28. int num_hits = _cull_hits.size();
  29. int left = p.result_max - p.result_count_overall;
  30. if (num_hits > left) {
  31. num_hits = left;
  32. }
  33. int out_n = p.result_count_overall;
  34. for (int n = 0; n < num_hits; n++) {
  35. uint32_t ref_id = _cull_hits[n];
  36. const ItemExtra &ex = _extra[ref_id];
  37. p.result_array[out_n] = ex.userdata;
  38. if (p.subindex_array) {
  39. p.subindex_array[out_n] = ex.subindex;
  40. }
  41. out_n++;
  42. }
  43. p.result_count = num_hits;
  44. p.result_count_overall += num_hits;
  45. }
  46. public:
  47. int cull_convex(CullParams &r_params, bool p_translate_hits = true) {
  48. _cull_hits.clear();
  49. r_params.result_count = 0;
  50. uint32_t tree_test_mask = 0;
  51. for (int n = 0; n < NUM_TREES; n++) {
  52. tree_test_mask <<= 1;
  53. if (!tree_test_mask) {
  54. tree_test_mask = 1;
  55. }
  56. if (_root_node_id[n] == BVHCommon::INVALID) {
  57. continue;
  58. }
  59. if (!(r_params.tree_collision_mask & tree_test_mask)) {
  60. continue;
  61. }
  62. _cull_convex_iterative(_root_node_id[n], r_params);
  63. }
  64. if (p_translate_hits) {
  65. _cull_translate_hits(r_params);
  66. }
  67. return r_params.result_count;
  68. }
  69. int cull_segment(CullParams &r_params, bool p_translate_hits = true) {
  70. _cull_hits.clear();
  71. r_params.result_count = 0;
  72. uint32_t tree_test_mask = 0;
  73. for (int n = 0; n < NUM_TREES; n++) {
  74. tree_test_mask <<= 1;
  75. if (!tree_test_mask) {
  76. tree_test_mask = 1;
  77. }
  78. if (_root_node_id[n] == BVHCommon::INVALID) {
  79. continue;
  80. }
  81. if (!(r_params.tree_collision_mask & tree_test_mask)) {
  82. continue;
  83. }
  84. _cull_segment_iterative(_root_node_id[n], r_params);
  85. }
  86. if (p_translate_hits) {
  87. _cull_translate_hits(r_params);
  88. }
  89. return r_params.result_count;
  90. }
  91. int cull_point(CullParams &r_params, bool p_translate_hits = true) {
  92. _cull_hits.clear();
  93. r_params.result_count = 0;
  94. uint32_t tree_test_mask = 0;
  95. for (int n = 0; n < NUM_TREES; n++) {
  96. tree_test_mask <<= 1;
  97. if (!tree_test_mask) {
  98. tree_test_mask = 1;
  99. }
  100. if (_root_node_id[n] == BVHCommon::INVALID) {
  101. continue;
  102. }
  103. if (!(r_params.tree_collision_mask & tree_test_mask)) {
  104. continue;
  105. }
  106. _cull_point_iterative(_root_node_id[n], r_params);
  107. }
  108. if (p_translate_hits) {
  109. _cull_translate_hits(r_params);
  110. }
  111. return r_params.result_count;
  112. }
  113. int cull_aabb(CullParams &r_params, bool p_translate_hits = true) {
  114. _cull_hits.clear();
  115. r_params.result_count = 0;
  116. uint32_t tree_test_mask = 0;
  117. for (int n = 0; n < NUM_TREES; n++) {
  118. tree_test_mask <<= 1;
  119. if (!tree_test_mask) {
  120. tree_test_mask = 1;
  121. }
  122. if (_root_node_id[n] == BVHCommon::INVALID) {
  123. continue;
  124. }
  125. // the tree collision mask determines which trees to collide test against
  126. if (!(r_params.tree_collision_mask & tree_test_mask)) {
  127. continue;
  128. }
  129. _cull_aabb_iterative(_root_node_id[n], r_params);
  130. }
  131. if (p_translate_hits) {
  132. _cull_translate_hits(r_params);
  133. }
  134. return r_params.result_count;
  135. }
  136. bool _cull_hits_full(const CullParams &p) {
  137. // instead of checking every hit, we can do a lazy check for this condition.
  138. // it isn't a problem if we write too much _cull_hits because they only the
  139. // result_max amount will be translated and outputted. But we might as
  140. // well stop our cull checks after the maximum has been reached.
  141. return (int)_cull_hits.size() >= p.result_max;
  142. }
  143. void _cull_hit(uint32_t p_ref_id, CullParams &p) {
  144. // take into account masks etc
  145. // this would be more efficient to do before plane checks,
  146. // but done here for ease to get started
  147. if (USE_PAIRS) {
  148. const ItemExtra &ex = _extra[p_ref_id];
  149. // user supplied function (for e.g. pairable types and pairable masks in the render tree)
  150. if (!USER_CULL_TEST_FUNCTION::user_cull_check(p.tester, ex.userdata)) {
  151. return;
  152. }
  153. }
  154. _cull_hits.push_back(p_ref_id);
  155. }
  156. bool _cull_segment_iterative(uint32_t p_node_id, CullParams &r_params) {
  157. // our function parameters to keep on a stack
  158. struct CullSegParams {
  159. uint32_t node_id;
  160. };
  161. // most of the iterative functionality is contained in this helper class
  162. BVH_IterativeInfo<CullSegParams> ii;
  163. // alloca must allocate the stack from this function, it cannot be allocated in the
  164. // helper class
  165. ii.stack = (CullSegParams *)alloca(ii.get_alloca_stacksize());
  166. // seed the stack
  167. ii.get_first()->node_id = p_node_id;
  168. CullSegParams csp;
  169. // while there are still more nodes on the stack
  170. while (ii.pop(csp)) {
  171. TNode &tnode = _nodes[csp.node_id];
  172. if (tnode.is_leaf()) {
  173. // lazy check for hits full up condition
  174. if (_cull_hits_full(r_params)) {
  175. return false;
  176. }
  177. TLeaf &leaf = _node_get_leaf(tnode);
  178. // test children individually
  179. for (int n = 0; n < leaf.num_items; n++) {
  180. const BVHABB_CLASS &aabb = leaf.get_aabb(n);
  181. if (aabb.intersects_segment(r_params.segment)) {
  182. uint32_t child_id = leaf.get_item_ref_id(n);
  183. // register hit
  184. _cull_hit(child_id, r_params);
  185. }
  186. }
  187. } else {
  188. // test children individually
  189. for (int n = 0; n < tnode.num_children; n++) {
  190. uint32_t child_id = tnode.children[n];
  191. const BVHABB_CLASS &child_abb = _nodes[child_id].aabb;
  192. if (child_abb.intersects_segment(r_params.segment)) {
  193. // add to the stack
  194. CullSegParams *child = ii.request();
  195. child->node_id = child_id;
  196. }
  197. }
  198. }
  199. } // while more nodes to pop
  200. // true indicates results are not full
  201. return true;
  202. }
  203. bool _cull_point_iterative(uint32_t p_node_id, CullParams &r_params) {
  204. // our function parameters to keep on a stack
  205. struct CullPointParams {
  206. uint32_t node_id;
  207. };
  208. // most of the iterative functionality is contained in this helper class
  209. BVH_IterativeInfo<CullPointParams> ii;
  210. // alloca must allocate the stack from this function, it cannot be allocated in the
  211. // helper class
  212. ii.stack = (CullPointParams *)alloca(ii.get_alloca_stacksize());
  213. // seed the stack
  214. ii.get_first()->node_id = p_node_id;
  215. CullPointParams cpp;
  216. // while there are still more nodes on the stack
  217. while (ii.pop(cpp)) {
  218. TNode &tnode = _nodes[cpp.node_id];
  219. // no hit with this node?
  220. if (!tnode.aabb.intersects_point(r_params.point)) {
  221. continue;
  222. }
  223. if (tnode.is_leaf()) {
  224. // lazy check for hits full up condition
  225. if (_cull_hits_full(r_params)) {
  226. return false;
  227. }
  228. TLeaf &leaf = _node_get_leaf(tnode);
  229. // test children individually
  230. for (int n = 0; n < leaf.num_items; n++) {
  231. if (leaf.get_aabb(n).intersects_point(r_params.point)) {
  232. uint32_t child_id = leaf.get_item_ref_id(n);
  233. // register hit
  234. _cull_hit(child_id, r_params);
  235. }
  236. }
  237. } else {
  238. // test children individually
  239. for (int n = 0; n < tnode.num_children; n++) {
  240. uint32_t child_id = tnode.children[n];
  241. // add to the stack
  242. CullPointParams *child = ii.request();
  243. child->node_id = child_id;
  244. }
  245. }
  246. } // while more nodes to pop
  247. // true indicates results are not full
  248. return true;
  249. }
  250. // Note: This is a very hot loop profiling wise. Take care when changing this and profile.
  251. bool _cull_aabb_iterative(uint32_t p_node_id, CullParams &r_params, bool p_fully_within = false) {
  252. // our function parameters to keep on a stack
  253. struct CullAABBParams {
  254. uint32_t node_id;
  255. bool fully_within;
  256. };
  257. // most of the iterative functionality is contained in this helper class
  258. BVH_IterativeInfo<CullAABBParams> ii;
  259. // alloca must allocate the stack from this function, it cannot be allocated in the
  260. // helper class
  261. ii.stack = (CullAABBParams *)alloca(ii.get_alloca_stacksize());
  262. // seed the stack
  263. ii.get_first()->node_id = p_node_id;
  264. ii.get_first()->fully_within = p_fully_within;
  265. CullAABBParams cap;
  266. // while there are still more nodes on the stack
  267. while (ii.pop(cap)) {
  268. TNode &tnode = _nodes[cap.node_id];
  269. if (tnode.is_leaf()) {
  270. // lazy check for hits full up condition
  271. if (_cull_hits_full(r_params)) {
  272. return false;
  273. }
  274. TLeaf &leaf = _node_get_leaf(tnode);
  275. // if fully within we can just add all items
  276. // as long as they pass mask checks
  277. if (cap.fully_within) {
  278. for (int n = 0; n < leaf.num_items; n++) {
  279. uint32_t child_id = leaf.get_item_ref_id(n);
  280. // register hit
  281. _cull_hit(child_id, r_params);
  282. }
  283. } else {
  284. // This section is the hottest area in profiling, so
  285. // is optimized highly
  286. // get this into a local register and preconverted to correct type
  287. int leaf_num_items = leaf.num_items;
  288. BVHABB_CLASS swizzled_tester;
  289. swizzled_tester.min = -r_params.abb.neg_max;
  290. swizzled_tester.neg_max = -r_params.abb.min;
  291. for (int n = 0; n < leaf_num_items; n++) {
  292. const BVHABB_CLASS &aabb = leaf.get_aabb(n);
  293. if (swizzled_tester.intersects_swizzled(aabb)) {
  294. uint32_t child_id = leaf.get_item_ref_id(n);
  295. // register hit
  296. _cull_hit(child_id, r_params);
  297. }
  298. }
  299. } // not fully within
  300. } else {
  301. if (!cap.fully_within) {
  302. // test children individually
  303. for (int n = 0; n < tnode.num_children; n++) {
  304. uint32_t child_id = tnode.children[n];
  305. const BVHABB_CLASS &child_abb = _nodes[child_id].aabb;
  306. if (child_abb.intersects(r_params.abb)) {
  307. // is the node totally within the aabb?
  308. bool fully_within = r_params.abb.is_other_within(child_abb);
  309. // add to the stack
  310. CullAABBParams *child = ii.request();
  311. // should always return valid child
  312. child->node_id = child_id;
  313. child->fully_within = fully_within;
  314. }
  315. }
  316. } else {
  317. for (int n = 0; n < tnode.num_children; n++) {
  318. uint32_t child_id = tnode.children[n];
  319. // add to the stack
  320. CullAABBParams *child = ii.request();
  321. // should always return valid child
  322. child->node_id = child_id;
  323. child->fully_within = true;
  324. }
  325. }
  326. }
  327. } // while more nodes to pop
  328. // true indicates results are not full
  329. return true;
  330. }
  331. // returns full up with results
  332. bool _cull_convex_iterative(uint32_t p_node_id, CullParams &r_params, bool p_fully_within = false) {
  333. // our function parameters to keep on a stack
  334. struct CullConvexParams {
  335. uint32_t node_id;
  336. bool fully_within;
  337. };
  338. // most of the iterative functionality is contained in this helper class
  339. BVH_IterativeInfo<CullConvexParams> ii;
  340. // alloca must allocate the stack from this function, it cannot be allocated in the
  341. // helper class
  342. ii.stack = (CullConvexParams *)alloca(ii.get_alloca_stacksize());
  343. // seed the stack
  344. ii.get_first()->node_id = p_node_id;
  345. ii.get_first()->fully_within = p_fully_within;
  346. // preallocate these as a once off to be reused
  347. uint32_t max_planes = r_params.hull.num_planes;
  348. uint32_t *plane_ids = (uint32_t *)alloca(sizeof(uint32_t) * max_planes);
  349. CullConvexParams ccp;
  350. // while there are still more nodes on the stack
  351. while (ii.pop(ccp)) {
  352. const TNode &tnode = _nodes[ccp.node_id];
  353. if (!ccp.fully_within) {
  354. typename BVHABB_CLASS::IntersectResult res = tnode.aabb.intersects_convex(r_params.hull);
  355. switch (res) {
  356. default: {
  357. continue; // miss, just move on to the next node in the stack
  358. } break;
  359. case BVHABB_CLASS::IR_PARTIAL: {
  360. } break;
  361. case BVHABB_CLASS::IR_FULL: {
  362. ccp.fully_within = true;
  363. } break;
  364. }
  365. } // if not fully within already
  366. if (tnode.is_leaf()) {
  367. // lazy check for hits full up condition
  368. if (_cull_hits_full(r_params)) {
  369. return false;
  370. }
  371. const TLeaf &leaf = _node_get_leaf(tnode);
  372. // if fully within, simply add all items to the result
  373. // (taking into account masks)
  374. if (ccp.fully_within) {
  375. for (int n = 0; n < leaf.num_items; n++) {
  376. uint32_t child_id = leaf.get_item_ref_id(n);
  377. // register hit
  378. _cull_hit(child_id, r_params);
  379. }
  380. } else {
  381. // we can either use a naive check of all the planes against the AABB,
  382. // or an optimized check, which finds in advance which of the planes can possibly
  383. // cut the AABB, and only tests those. This can be much faster.
  384. #define BVH_CONVEX_CULL_OPTIMIZED
  385. #ifdef BVH_CONVEX_CULL_OPTIMIZED
  386. // first find which planes cut the aabb
  387. uint32_t num_planes = tnode.aabb.find_cutting_planes(r_params.hull, plane_ids);
  388. BVH_ASSERT(num_planes <= max_planes);
  389. //#define BVH_CONVEX_CULL_OPTIMIZED_RIGOR_CHECK
  390. #ifdef BVH_CONVEX_CULL_OPTIMIZED_RIGOR_CHECK
  391. // rigorous check
  392. uint32_t results[MAX_ITEMS];
  393. uint32_t num_results = 0;
  394. #endif
  395. // test children individually
  396. for (int n = 0; n < leaf.num_items; n++) {
  397. //const Item &item = leaf.get_item(n);
  398. const BVHABB_CLASS &aabb = leaf.get_aabb(n);
  399. if (aabb.intersects_convex_optimized(r_params.hull, plane_ids, num_planes)) {
  400. uint32_t child_id = leaf.get_item_ref_id(n);
  401. #ifdef BVH_CONVEX_CULL_OPTIMIZED_RIGOR_CHECK
  402. results[num_results++] = child_id;
  403. #endif
  404. // register hit
  405. _cull_hit(child_id, r_params);
  406. }
  407. }
  408. #ifdef BVH_CONVEX_CULL_OPTIMIZED_RIGOR_CHECK
  409. uint32_t test_count = 0;
  410. for (int n = 0; n < leaf.num_items; n++) {
  411. const BVHABB_CLASS &aabb = leaf.get_aabb(n);
  412. if (aabb.intersects_convex_partial(r_params.hull)) {
  413. uint32_t child_id = leaf.get_item_ref_id(n);
  414. CRASH_COND(child_id != results[test_count++]);
  415. CRASH_COND(test_count > num_results);
  416. }
  417. }
  418. #endif
  419. #else
  420. // not BVH_CONVEX_CULL_OPTIMIZED
  421. // test children individually
  422. for (int n = 0; n < leaf.num_items; n++) {
  423. const BVHABB_CLASS &aabb = leaf.get_aabb(n);
  424. if (aabb.intersects_convex_partial(r_params.hull)) {
  425. uint32_t child_id = leaf.get_item_ref_id(n);
  426. // full up with results? exit early, no point in further testing
  427. if (!_cull_hit(child_id, r_params)) {
  428. return false;
  429. }
  430. }
  431. }
  432. #endif // BVH_CONVEX_CULL_OPTIMIZED
  433. } // if not fully within
  434. } else {
  435. for (int n = 0; n < tnode.num_children; n++) {
  436. uint32_t child_id = tnode.children[n];
  437. // add to the stack
  438. CullConvexParams *child = ii.request();
  439. // should always return valid child
  440. child->node_id = child_id;
  441. child->fully_within = ccp.fully_within;
  442. }
  443. }
  444. } // while more nodes to pop
  445. // true indicates results are not full
  446. return true;
  447. }