diff --git a/src/aabb.rs b/src/aabb.rs new file mode 100644 index 0000000..65e783f --- /dev/null +++ b/src/aabb.rs @@ -0,0 +1,46 @@ +use crate::integer::Planar64Vec3; + +#[derive(Clone)] +pub struct Aabb{ + min:Planar64Vec3, + max:Planar64Vec3, +} + +impl Default for Aabb { + fn default()->Self { + Self{min:Planar64Vec3::MAX,max:Planar64Vec3::MIN} + } +} + +impl Aabb{ + pub fn grow(&mut self,point:Planar64Vec3){ + self.min=self.min.min(point); + self.max=self.max.max(point); + } + pub fn join(&mut self,aabb:&Aabb){ + self.min=self.min.min(aabb.min); + self.max=self.max.max(aabb.max); + } + pub fn inflate(&mut self,hs:Planar64Vec3){ + self.min-=hs; + self.max+=hs; + } + pub fn intersects(&self,aabb:&Aabb)->bool{ + (self.min.cmplt(aabb.max)&aabb.min.cmplt(self.max)).all() + } + pub fn size(&self)->Planar64Vec3{ + self.max-self.min + } + pub fn center(&self)->Planar64Vec3{ + self.min.midpoint(self.max) + } + //probably use floats for area & volume because we don't care about precision + // pub fn area_weight(&self)->f32{ + // let d=self.max-self.min; + // d.x*d.y+d.y*d.z+d.z*d.x + // } + // pub fn volume(&self)->f32{ + // let d=self.max-self.min; + // d.x*d.y*d.z + // } +} \ No newline at end of file diff --git a/src/bvh.rs b/src/bvh.rs new file mode 100644 index 0000000..f4c9bc6 --- /dev/null +++ b/src/bvh.rs @@ -0,0 +1,123 @@ +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 +enum BvhNodeContent{ + Branch(Vec), + Leaf(usize), +} +impl Default for BvhNodeContent{ + fn default()->Self{ + Self::Branch(Vec::new()) + } +} +#[derive(Default)] +pub struct BvhNode{ + content:BvhNodeContent, + aabb:Aabb, +} + +impl BvhNode{ + pub fn the_tester(&self,aabb:&Aabb,f:&mut F){ + match &self.content{ + &BvhNodeContent::Leaf(model)=>f(model), + BvhNodeContent::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 generate_bvh(boxen:Vec)->BvhNode{ + generate_bvh_node(boxen.into_iter().enumerate().collect()) +} + +fn generate_bvh_node(boxen:Vec<(usize,Aabb)>)->BvhNode{ + let n=boxen.len(); + if n<20{ + let mut aabb=Aabb::default(); + let nodes=boxen.into_iter().map(|b|{ + aabb.join(&b.1); + BvhNode{ + content:BvhNodeContent::Leaf(b.0), + aabb:b.1, + } + }).collect(); + BvhNode{ + content:BvhNodeContent::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(){ + 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{ + pub time:Time, + pub instruction:I, +} + +pub trait InstructionEmitter{ + fn next_instruction(&self,time_limit:Time)->Option>; +} +pub trait InstructionConsumer{ + fn process_instruction(&mut self, instruction:TimedInstruction); +} + +//PROPER PRIVATE FIELDS!!! +pub struct InstructionCollector{ + time:Time, + instruction:Option, +} +impl InstructionCollector{ + pub fn new(time:Time)->Self{ + Self{ + time, + instruction:None + } + } + #[inline] + pub fn time(&self)->Time{ + self.time + } + pub fn collect(&mut self,instruction:Option>){ + match instruction{ + Some(unwrap_instruction)=>{ + if unwrap_instruction.time(), + } + } + pub fn instruction(self)->Option>{ + //STEAL INSTRUCTION AND DESTROY INSTRUCTIONCOLLECTOR + match self.instruction{ + Some(instruction)=>Some(TimedInstruction{ + time:self.time, + instruction + }), + None=>None, + } + } +} \ No newline at end of file diff --git a/src/integer.rs b/src/integer.rs new file mode 100644 index 0000000..b04376a --- /dev/null +++ b/src/integer.rs @@ -0,0 +1,1054 @@ +//integer units +#[derive(Clone,Copy,Hash,Eq,PartialEq,PartialOrd,Debug)] +pub struct Time(i64); +impl Time{ + pub const MIN:Self=Self(i64::MIN); + pub const MAX:Self=Self(i64::MAX); + pub const ZERO:Self=Self(0); + pub const ONE_SECOND:Self=Self(1_000_000_000); + pub const ONE_MILLISECOND:Self=Self(1_000_000); + pub const ONE_MICROSECOND:Self=Self(1_000); + pub const ONE_NANOSECOND:Self=Self(1); + #[inline] + pub fn from_secs(num:i64)->Self{ + Self(Self::ONE_SECOND.0*num) + } + #[inline] + pub fn from_millis(num:i64)->Self{ + Self(Self::ONE_MILLISECOND.0*num) + } + #[inline] + pub fn from_micros(num:i64)->Self{ + Self(Self::ONE_MICROSECOND.0*num) + } + #[inline] + pub fn from_nanos(num:i64)->Self{ + Self(Self::ONE_NANOSECOND.0*num) + } + //should I have checked subtraction? force all time variables to be positive? + #[inline] + pub fn nanos(&self)->i64{ + self.0 + } +} +impl From for Time{ + #[inline] + fn from(value:Planar64)->Self{ + Time((((value.0 as i128)*1_000_000_000)>>32) as i64) + } +} +impl std::fmt::Display for Time{ + #[inline] + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{}s+{:09}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0) + } +} +impl std::default::Default for Time{ + fn default()->Self{ + Self(0) + } +} +impl std::ops::Neg for Time{ + type Output=Time; + #[inline] + fn neg(self)->Self::Output { + Time(-self.0) + } +} +impl std::ops::Add