common: bvh: arbitrary traversal

This commit is contained in:
2026-03-13 08:52:22 -07:00
parent 36447a76cf
commit 797b5d2100

View File

@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
use crate::aabb::Aabb; use crate::aabb::Aabb;
use crate::ray::Ray; use crate::ray::Ray;
use crate::integer::{Ratio,Planar64}; use crate::integer::{Ratio,Planar64,Planar64Vec3};
use crate::instruction::{InstructionCollector,TimedInstruction}; use crate::instruction::{InstructionCollector,TimedInstruction};
//da algaritum //da algaritum
@@ -140,33 +140,34 @@ impl<L> BvhNode<L>{
}, },
} }
} }
fn populate_nodes<'a,T,F>( fn populate_nodes<'a,T,IntersectLeaf,IntersectAabb>(
&'a self, &'a self,
collector:&mut InstructionCollector<&'a L,Ratio<Planar64,Planar64>>, start_point:Planar64Vec3,
nodes:&mut BTreeMap<Ratio<Planar64,Planar64>,&'a BvhNode<L>>, collector:&mut InstructionCollector<&'a L,T>,
ray:&Ray, nodes:&mut BTreeMap<T,&'a BvhNode<L>>,
start_time:Ratio<Planar64,Planar64>, start_time:T,
f:&F, intersect_leaf:&IntersectLeaf,
intersect_aabb:&IntersectAabb,
) )
where where
T:Ord+Copy, T:Ord+Copy,
Ratio<Planar64,Planar64>:From<T>, IntersectLeaf:Fn(&L)->Option<T>,
F:Fn(&L,&Ray)->Option<T>, IntersectAabb:Fn(&Aabb)->Option<T>,
{ {
match &self.content{ match &self.content{
RecursiveContent::Leaf(leaf)=>if let Some(time)=f(leaf,ray){ RecursiveContent::Leaf(leaf)=>if let Some(time)=intersect_leaf(leaf){
let ins=TimedInstruction{time:time.into(),instruction:leaf}; let ins=TimedInstruction{time,instruction:leaf};
if start_time.lt_ratio(ins.time)&&ins.time.lt_ratio(collector.time()){ if start_time<ins.time&&ins.time<collector.time(){
collector.collect(Some(ins)); collector.collect(Some(ins));
} }
}, },
RecursiveContent::Branch(children)=>for child in children{ RecursiveContent::Branch(children)=>for child in children{
if child.aabb.contains(ray.origin){ if child.aabb.contains(start_point){
child.populate_nodes(collector,nodes,ray,start_time,f); child.populate_nodes(start_point,collector,nodes,start_time,intersect_leaf,intersect_aabb);
}else{ }else{
// Am I an upcoming superstar? // Am I an upcoming superstar?
if let Some(t)=intersect_aabb(ray,&child.aabb){ if let Some(t)=intersect_aabb(&child.aabb){
if start_time.lt_ratio(t)&&t.lt_ratio(collector.time()){ if start_time<t&&t<collector.time(){
nodes.insert(t,child); nodes.insert(t,child);
} }
} }
@@ -174,27 +175,29 @@ impl<L> BvhNode<L>{
}, },
} }
} }
pub fn sample_ray<T,F>( /// Traverse the BVH using the given sampling functions.
/// Nodes are tested in order of T returned by IntersectAabb.
/// The algorithm ends when T for the next node to test is
/// greater than the current best collected T from IntersectLeaf.
pub fn traverse<T,IntersectLeaf,IntersectAabb>(
&self, &self,
ray:&Ray, start_point:Planar64Vec3,
start_time:T, start_time:T,
time_limit:T, time_limit:T,
f:F, intersect_leaf:IntersectLeaf,
intersect_aabb:IntersectAabb,
)->Option<(T,&L)> )->Option<(T,&L)>
where where
T:Ord+Copy, T:Ord+Copy,
T:From<Ratio<Planar64,Planar64>>, IntersectLeaf:Fn(&L)->Option<T>,
Ratio<Planar64,Planar64>:From<T>, IntersectAabb:Fn(&Aabb)->Option<T>,
F:Fn(&L,&Ray)->Option<T>,
{ {
// source of nondeterminism when Aabb boundaries are coplanar // source of nondeterminism when Aabb boundaries are coplanar
let mut nodes=BTreeMap::new(); let mut nodes=BTreeMap::new();
let start_time=start_time.into();
let time_limit=time_limit.into();
let mut collector=InstructionCollector::new(time_limit); let mut collector=InstructionCollector::new(time_limit);
// break open all nodes that contain ray.origin and populate nodes with future intersection times // break open all nodes that contain ray.origin and populate nodes with future intersection times
self.populate_nodes(&mut collector,&mut nodes,ray,start_time,&f); self.populate_nodes(start_point,&mut collector,&mut nodes,start_time,&intersect_leaf,&intersect_aabb);
// swim through nodes one at a time // swim through nodes one at a time
while let Some((t,node))=nodes.pop_first(){ while let Some((t,node))=nodes.pop_first(){
@@ -202,18 +205,18 @@ impl<L> BvhNode<L>{
break; break;
} }
match &node.content{ match &node.content{
RecursiveContent::Leaf(leaf)=>if let Some(time)=f(leaf,ray){ RecursiveContent::Leaf(leaf)=>if let Some(time)=intersect_leaf(leaf){
let ins=TimedInstruction{time:time.into(),instruction:leaf}; let ins=TimedInstruction{time:time.into(),instruction:leaf};
// this lower bound can also be omitted // this lower bound can also be omitted
// but it causes type inference errors lol // but it causes type inference errors lol
if start_time.lt_ratio(ins.time)&&ins.time.lt_ratio(collector.time()){ if start_time<ins.time&&ins.time<collector.time(){
collector.collect(Some(ins)); collector.collect(Some(ins));
} }
}, },
// break open the node and predict collisions with the child nodes // break open the node and predict collisions with the child nodes
RecursiveContent::Branch(children)=>for child in children{ RecursiveContent::Branch(children)=>for child in children{
// Am I an upcoming superstar? // Am I an upcoming superstar?
if let Some(t)=intersect_aabb(ray,&child.aabb){ if let Some(t)=intersect_aabb(&child.aabb){
// we don't need to check the lower bound // we don't need to check the lower bound
// because child aabbs are guaranteed to be within the parent bounds. // because child aabbs are guaranteed to be within the parent bounds.
if t<collector.time(){ if t<collector.time(){