use crate::aabb::Aabb; //da algaritum //lista boxens //sort by {minx,maxx,miny,maxy,minz,maxz} (6 lists) //find the sets that minimizes the sum of surface areas //splitting is done when the minimum split sum of surface areas is larger than the node's own surface area //start with bisection into octrees because a bad bvh is still 1000x better than no bvh //sort the centerpoints on each axis (3 lists) //bv is put into octant based on whether it is upper or lower in each list pub enum RecursiveContent<R,T>{ Branch(Vec<R>), Leaf(T), } impl<R,T> Default for RecursiveContent<R,T>{ fn default()->Self{ Self::Branch(Vec::new()) } } pub struct BvhNode<T>{ content:RecursiveContent<BvhNode<T>,T>, aabb:Aabb, } impl<T> Default for BvhNode<T>{ fn default()->Self{ Self{ content:Default::default(), aabb:Aabb::default(), } } } pub struct BvhWeightNode<W,T>{ content:RecursiveContent<BvhWeightNode<W,T>,T>, weight:W, aabb:Aabb, } impl<T> BvhNode<T>{ pub fn the_tester<F:FnMut(&T)>(&self,aabb:&Aabb,f:&mut F){ match &self.content{ RecursiveContent::Leaf(model)=>f(model), RecursiveContent::Branch(children)=>for child in children{ //this test could be moved outside the match statement //but that would test the root node aabb //you're probably not going to spend a lot of time outside the map, //so the test is extra work for nothing if aabb.intersects(&child.aabb){ child.the_tester(aabb,f); } }, } } pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){ match self.content{ RecursiveContent::Leaf(model)=>f(model), RecursiveContent::Branch(children)=>for child in children{ child.into_visitor(f) }, } } pub fn weigh_contents<W:Copy+std::iter::Sum<W>,F:Fn(&T)->W>(self,f:&F)->BvhWeightNode<W,T>{ match self.content{ RecursiveContent::Leaf(model)=>BvhWeightNode{ weight:f(&model), content:RecursiveContent::Leaf(model), aabb:self.aabb, }, RecursiveContent::Branch(children)=>{ let branch:Vec<BvhWeightNode<W,T>>=children.into_iter().map(|child| child.weigh_contents(f) ).collect(); BvhWeightNode{ weight:branch.iter().map(|node|node.weight).sum(), content:RecursiveContent::Branch(branch), aabb:self.aabb, } }, } } } impl <W,T> BvhWeightNode<W,T>{ pub const fn weight(&self)->&W{ &self.weight } pub const fn aabb(&self)->&Aabb{ &self.aabb } pub fn into_content(self)->RecursiveContent<BvhWeightNode<W,T>,T>{ self.content } pub fn into_visitor<F:FnMut(T)>(self,f:&mut F){ match self.content{ RecursiveContent::Leaf(model)=>f(model), RecursiveContent::Branch(children)=>for child in children{ child.into_visitor(f) }, } } } pub fn generate_bvh<T>(boxen:Vec<(T,Aabb)>)->BvhNode<T>{ generate_bvh_node(boxen,false) } fn generate_bvh_node<T>(boxen:Vec<(T,Aabb)>,force:bool)->BvhNode<T>{ let n=boxen.len(); if force||n<20{ let mut aabb=Aabb::default(); let nodes=boxen.into_iter().map(|b|{ aabb.join(&b.1); BvhNode{ content:RecursiveContent::Leaf(b.0), aabb:b.1, } }).collect(); BvhNode{ content:RecursiveContent::Branch(nodes), aabb, } }else{ let mut octant=std::collections::HashMap::with_capacity(n);//this ids which octant the boxen is put in let mut sort_x=Vec::with_capacity(n); let mut sort_y=Vec::with_capacity(n); let mut sort_z=Vec::with_capacity(n); for (i,(_,aabb)) in boxen.iter().enumerate(){ let center=aabb.center(); octant.insert(i,0); sort_x.push((i,center.x())); sort_y.push((i,center.y())); sort_z.push((i,center.z())); } sort_x.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1)); sort_y.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1)); sort_z.sort_by(|tup0,tup1|tup0.1.cmp(&tup1.1)); let h=n/2; let median_x=sort_x[h].1; let median_y=sort_y[h].1; let median_z=sort_z[h].1; for (i,c) in sort_x{ if median_x<c{ octant.insert(i,octant[&i]+1<<0); } } for (i,c) in sort_y{ if median_y<c{ octant.insert(i,octant[&i]+1<<1); } } for (i,c) in sort_z{ if median_z<c{ octant.insert(i,octant[&i]+1<<2); } } //generate lists for unique octant values let mut list_list=Vec::with_capacity(8); let mut octant_list=Vec::with_capacity(8); for (i,(data,aabb)) in boxen.into_iter().enumerate(){ let octant_id=octant[&i]; let list_id=if let Some(list_id)=octant_list.iter().position(|&id|id==octant_id){ list_id }else{ let list_id=list_list.len(); octant_list.push(octant_id); list_list.push(Vec::new()); list_id }; list_list[list_id].push((data,aabb)); } let mut aabb=Aabb::default(); if list_list.len()==1{ generate_bvh_node(list_list.remove(0),true) }else{ BvhNode{ content:RecursiveContent::Branch( list_list.into_iter().map(|b|{ let node=generate_bvh_node(b,false); aabb.join(&node.aabb); node }).collect() ), aabb, } } } }