@@ -65,9 +65,9 @@ void LBVH::build(
6565
6666 compute_mesh_aabb (mesh_aabb.min , mesh_aabb.max );
6767
68- init_bvh (vertex_boxes, vertex_bvh);
69- init_bvh (edge_boxes, edge_bvh);
70- init_bvh (face_boxes, face_bvh);
68+ init_bvh (vertex_boxes, vertex_bvh, vertex_rightmost_leaves );
69+ init_bvh (edge_boxes, edge_bvh, edge_rightmost_leaves );
70+ init_bvh (face_boxes, face_bvh, face_rightmost_leaves );
7171
7272 // Copy edge and face vertex ids for access during traversal
7373 {
@@ -199,7 +199,8 @@ namespace {
199199 }
200200} // namespace
201201
202- void LBVH::init_bvh (const AABBs& boxes, Nodes& lbvh) const
202+ void LBVH::init_bvh (
203+ const AABBs& boxes, Nodes& lbvh, RightmostLeaves& rightmost_leaves) const
203204{
204205 if (boxes.empty ()) {
205206 return ;
@@ -252,6 +253,10 @@ void LBVH::init_bvh(const AABBs& boxes, Nodes& lbvh) const
252253 assert (boxes.size () <= std::numeric_limits<int >::max ());
253254 const int LEAF_OFFSET = int (boxes.size ()) - 1 ;
254255
256+ if (rightmost_leaves.size () != lbvh.size ()) {
257+ rightmost_leaves.resize (lbvh.size ());
258+ }
259+
255260 LBVH::ConstructionInfos construction_infos (lbvh.size ());
256261 {
257262 IPC_TOOLKIT_PROFILE_BLOCK (" build_hierarchy" );
@@ -269,6 +274,8 @@ void LBVH::init_bvh(const AABBs& boxes, Nodes& lbvh) const
269274 leaf_node.primitive_id = morton_codes[i].box_id ;
270275 leaf_node.is_inner_marker = 0 ;
271276 lbvh[LEAF_OFFSET + i] = leaf_node; // Store leaf
277+ // A leaf's rightmost leaf is itself
278+ rightmost_leaves[LEAF_OFFSET + i] = i;
272279 }
273280
274281 if (i < LEAF_OFFSET) {
@@ -345,6 +352,11 @@ void LBVH::init_bvh(const AABBs& boxes, Nodes& lbvh) const
345352 lbvh[node_idx].aabb_max =
346353 child_a.aabb_max .max (child_b.aabb_max );
347354
355+ // Compute rightmost leaf: max of children's rightmost
356+ rightmost_leaves[node_idx] = std::max (
357+ rightmost_leaves[lbvh[node_idx].left ],
358+ rightmost_leaves[lbvh[node_idx].right ]);
359+
348360 if (node_idx == 0 ) {
349361 break ; // root node
350362 }
@@ -360,16 +372,19 @@ void LBVH::clear()
360372 BroadPhase::clear ();
361373 // Clear BVH nodes
362374 vertex_bvh.clear ();
375+ vertex_rightmost_leaves.clear ();
363376 edge_bvh.clear ();
377+ edge_rightmost_leaves.clear ();
364378 face_bvh.clear ();
379+ face_rightmost_leaves.clear ();
365380
366381 // Clear vertex IDs
367382 edge_vertex_ids.clear ();
368383 face_vertex_ids.clear ();
369384}
370385
371386namespace {
372- template <typename Candidate, bool swap_order, bool triangular >
387+ template <typename Candidate, bool swap_order>
373388 inline void attempt_add_candidate (
374389 const LBVH::Node& query,
375390 const LBVH::Node& node,
@@ -381,12 +396,6 @@ namespace {
381396 std::swap (i, j);
382397 }
383398
384- if constexpr (triangular) {
385- if (i >= j) {
386- return ;
387- }
388- }
389-
390399 if (!can_collide (i, j)) {
391400 return ;
392401 }
@@ -397,7 +406,9 @@ namespace {
397406 template <typename Candidate, bool swap_order, bool triangular>
398407 void traverse_lbvh (
399408 const LBVH::Node& query,
409+ const size_t query_leaf_idx,
400410 const LBVH::Nodes& lbvh,
411+ const LBVH::RightmostLeaves& rightmost_leaves,
401412 const std::function<bool (size_t , size_t )>& can_collide,
402413 std::vector<Candidate>& candidates)
403414 {
@@ -413,8 +424,11 @@ namespace {
413424
414425 if (lbvh.size () == 1 ) { // Single node case (only root)
415426 assert (node.is_leaf ()); // Only one node, so it must be a leaf
427+ if constexpr (triangular) {
428+ break ; // No self-collision if only one node
429+ }
416430 if (node.intersects (query)) {
417- attempt_add_candidate<Candidate, swap_order, triangular >(
431+ attempt_add_candidate<Candidate, swap_order>(
418432 query, node, can_collide, candidates);
419433 }
420434 break ;
@@ -431,16 +445,29 @@ namespace {
431445
432446 const LBVH::Node& child_l = lbvh[node.left ];
433447 const LBVH::Node& child_r = lbvh[node.right ];
434- const bool intersects_l = child_l.intersects (query);
435- const bool intersects_r = child_r.intersects (query);
448+ bool intersects_l = child_l.intersects (query);
449+ bool intersects_r = child_r.intersects (query);
450+
451+ // Ignore overlap if the subtree is fully on the
452+ // left-hand side of the query (triangular traversal only).
453+ if constexpr (triangular) {
454+ if (intersects_l
455+ && rightmost_leaves[node.left ] <= query_leaf_idx) {
456+ intersects_l = false ;
457+ }
458+ if (intersects_r
459+ && rightmost_leaves[node.right ] <= query_leaf_idx) {
460+ intersects_r = false ;
461+ }
462+ }
436463
437464 // Query overlaps a leaf node => report collision.
438465 if (intersects_l && child_l.is_leaf ()) {
439- attempt_add_candidate<Candidate, swap_order, triangular >(
466+ attempt_add_candidate<Candidate, swap_order>(
440467 query, child_l, can_collide, candidates);
441468 }
442469 if (intersects_r && child_r.is_leaf ()) {
443- attempt_add_candidate<Candidate, swap_order, triangular >(
470+ attempt_add_candidate<Candidate, swap_order>(
444471 query, child_r, can_collide, candidates);
445472 }
446473
@@ -468,8 +495,10 @@ namespace {
468495 template <typename Candidate, bool swap_order, bool triangular>
469496 void traverse_lbvh_simd (
470497 const LBVH::Node* queries,
498+ const size_t first_query_leaf_idx,
471499 const size_t n_queries,
472500 const LBVH::Nodes& lbvh,
501+ const LBVH::RightmostLeaves& rightmost_leaves,
473502 const std::function<bool (size_t , size_t )>& can_collide,
474503 std::vector<Candidate>& candidates)
475504 {
@@ -518,6 +547,9 @@ namespace {
518547
519548 if (lbvh.size () == 1 ) { // Single node case (only root)
520549 assert (node.is_leaf ()); // Only one node, so it must be a leaf
550+ if constexpr (triangular) {
551+ break ; // No self-collision if only one node
552+ }
521553 // Check intersection with all queries simultaneously
522554 const xs::batch_bool<float > intersects =
523555 (node.aabb_min .x () <= q_max_x)
@@ -529,8 +561,7 @@ namespace {
529561 if (xs::any (intersects)) {
530562 for (int k = 0 ; k < n_queries; ++k) {
531563 if (intersects.get (k)) {
532- attempt_add_candidate<
533- Candidate, swap_order, triangular>(
564+ attempt_add_candidate<Candidate, swap_order>(
534565 queries[k], node, can_collide, candidates);
535566 }
536567 }
@@ -552,7 +583,7 @@ namespace {
552583
553584 // 1. Intersect multiple queries at once
554585 // (child_l.min <= query.max) && (query.min <= child_l.max)
555- const xs::batch_bool<float > intersects_l =
586+ xs::batch_bool<float > intersects_l =
556587 (child_l.aabb_min .x () <= q_max_x)
557588 & (child_l.aabb_min .y () <= q_max_y)
558589 & (child_l.aabb_min .z () <= q_max_z)
@@ -562,32 +593,57 @@ namespace {
562593
563594 // 2. Intersect multiple queries at once
564595 // (child_r.min <= query.max) && (query.min <= child_r.max)
565- const xs::batch_bool<float > intersects_r =
596+ xs::batch_bool<float > intersects_r =
566597 (child_r.aabb_min .x () <= q_max_x)
567598 & (child_r.aabb_min .y () <= q_max_y)
568599 & (child_r.aabb_min .z () <= q_max_z)
569600 & (q_min_x <= child_r.aabb_max .x ())
570601 & (q_min_y <= child_r.aabb_max .y ())
571602 & (q_min_z <= child_r.aabb_max .z ());
572603
604+ // Ignore overlap if the subtree is fully on the left-hand side
605+ // of all queries (triangular traversal only).
606+ // We use first_query_leaf_idx (the smallest query leaf index
607+ // in the SIMD batch) for a conservative check: if all leaves
608+ // in the subtree are <= the smallest query, they are also <=
609+ // every other query in the batch.
610+ if constexpr (triangular) {
611+ if (rightmost_leaves[node.left ] <= first_query_leaf_idx) {
612+ intersects_l = xs::batch_bool<float >(false );
613+ }
614+ if (rightmost_leaves[node.right ] <= first_query_leaf_idx) {
615+ intersects_r = xs::batch_bool<float >(false );
616+ }
617+ }
618+
573619 const bool any_intersects_l = xs::any (intersects_l);
574620 const bool any_intersects_r = xs::any (intersects_r);
575621
576622 // Query overlaps a leaf node => report collision
577623 if (any_intersects_l && child_l.is_leaf ()) {
578624 for (int k = 0 ; k < n_queries; ++k) {
625+ if constexpr (triangular) {
626+ if (rightmost_leaves[node.left ]
627+ <= first_query_leaf_idx + k) {
628+ continue ;
629+ }
630+ }
579631 if (intersects_l.get (k)) {
580- attempt_add_candidate<
581- Candidate, swap_order, triangular>(
632+ attempt_add_candidate<Candidate, swap_order>(
582633 queries[k], child_l, can_collide, candidates);
583634 }
584635 }
585636 }
586637 if (any_intersects_r && child_r.is_leaf ()) {
587638 for (int k = 0 ; k < n_queries; ++k) {
639+ if constexpr (triangular) {
640+ if (rightmost_leaves[node.right ]
641+ <= first_query_leaf_idx + k) {
642+ continue ;
643+ }
644+ }
588645 if (intersects_r.get (k)) {
589- attempt_add_candidate<
590- Candidate, swap_order, triangular>(
646+ attempt_add_candidate<Candidate, swap_order>(
591647 queries[k], child_r, can_collide, candidates);
592648 }
593649 }
@@ -620,6 +676,7 @@ namespace {
620676 void independent_traversal (
621677 const LBVH::Nodes& source,
622678 const LBVH::Nodes& target,
679+ const LBVH::RightmostLeaves& rightmost_leaves,
623680 const std::function<bool (size_t , size_t )>& can_collide,
624681 tbb::enumerable_thread_specific<std::vector<Candidate>>& storage)
625682 {
@@ -655,14 +712,14 @@ namespace {
655712 if constexpr (use_simd) {
656713 assert (actual_end - idx >= 1 );
657714 traverse_lbvh_simd<Candidate, swap_order, triangular>(
658- &source[source_leaf_offset + idx],
715+ &source[source_leaf_offset + idx], idx,
659716 std::min (SIMD_SIZE, actual_end - idx), target,
660- can_collide, local_candidates);
717+ rightmost_leaves, can_collide, local_candidates);
661718 } else {
662719#endif
663720 traverse_lbvh<Candidate, swap_order, triangular>(
664- source[source_leaf_offset + idx], target,
665- can_collide, local_candidates);
721+ source[source_leaf_offset + idx], idx, target,
722+ rightmost_leaves, can_collide, local_candidates);
666723#ifdef IPC_TOOLKIT_WITH_SIMD
667724 }
668725#endif
@@ -675,6 +732,7 @@ template <typename Candidate, bool swap_order, bool triangular>
675732void LBVH::detect_candidates (
676733 const Nodes& source,
677734 const Nodes& target,
735+ const RightmostLeaves& rightmost_leaves,
678736 const std::function<bool (size_t , size_t )>& can_collide,
679737 std::vector<Candidate>& candidates)
680738{
@@ -685,7 +743,7 @@ void LBVH::detect_candidates(
685743 tbb::enumerable_thread_specific<std::vector<Candidate>> storage;
686744
687745 independent_traversal<Candidate, swap_order, triangular>(
688- source, target, can_collide, storage);
746+ source, target, rightmost_leaves, can_collide, storage);
689747
690748 merge_thread_local_vectors (storage, candidates);
691749}
@@ -699,7 +757,8 @@ void LBVH::detect_vertex_vertex_candidates(
699757
700758 IPC_TOOLKIT_PROFILE_BLOCK (" LBVH::detect_vertex_vertex_candidates" );
701759
702- detect_candidates (vertex_bvh, can_vertices_collide, candidates);
760+ detect_candidates (
761+ vertex_bvh, vertex_rightmost_leaves, can_vertices_collide, candidates);
703762}
704763
705764void LBVH::detect_edge_vertex_candidates (
@@ -714,7 +773,7 @@ void LBVH::detect_edge_vertex_candidates(
714773 // In 2D and for codimensional edge-vertex collisions, there are more
715774 // vertices than edges, so we want to iterate over the edges.
716775 detect_candidates (
717- edge_bvh, vertex_bvh,
776+ edge_bvh, vertex_bvh, vertex_rightmost_leaves,
718777 std::bind (&LBVH::can_edge_vertex_collide, this , _1, _2), candidates);
719778}
720779
@@ -728,8 +787,8 @@ void LBVH::detect_edge_edge_candidates(
728787 IPC_TOOLKIT_PROFILE_BLOCK (" LBVH::detect_edge_edge_candidates" );
729788
730789 detect_candidates (
731- edge_bvh, std::bind (&LBVH::can_edges_collide, this , _1, _2) ,
732- candidates);
790+ edge_bvh, edge_rightmost_leaves ,
791+ std::bind (&LBVH::can_edges_collide, this , _1, _2), candidates);
733792}
734793
735794void LBVH::detect_face_vertex_candidates (
@@ -743,7 +802,7 @@ void LBVH::detect_face_vertex_candidates(
743802
744803 // The ratio vertices:faces is 1:2, so we want to iterate over the vertices.
745804 detect_candidates<FaceVertexCandidate, /* swap_order=*/ true >(
746- vertex_bvh, face_bvh,
805+ vertex_bvh, face_bvh, face_rightmost_leaves,
747806 std::bind (&LBVH::can_face_vertex_collide, this , _1, _2), candidates);
748807}
749808
@@ -758,7 +817,7 @@ void LBVH::detect_edge_face_candidates(
758817
759818 // The ratio edges:faces is 3:2, so we want to iterate over the faces.
760819 detect_candidates<EdgeFaceCandidate, /* swap_order=*/ true >(
761- face_bvh, edge_bvh,
820+ face_bvh, edge_bvh, edge_rightmost_leaves,
762821 std::bind (&LBVH::can_edge_face_collide, this , _1, _2), candidates);
763822}
764823
@@ -771,8 +830,8 @@ void LBVH::detect_face_face_candidates(
771830
772831 IPC_TOOLKIT_PROFILE_BLOCK (" LBVH::detect_face_face_candidates" );
773832 detect_candidates (
774- face_bvh, std::bind (&LBVH::can_faces_collide, this , _1, _2) ,
775- candidates);
833+ face_bvh, face_rightmost_leaves ,
834+ std::bind (&LBVH::can_faces_collide, this , _1, _2), candidates);
776835}
777836
778837// ============================================================================
0 commit comments