diff --git a/world/src/site2/gen.rs b/world/src/site2/gen.rs index d7a9e17345..0fd7964a3c 100644 --- a/world/src/site2/gen.rs +++ b/world/src/site2/gen.rs @@ -82,6 +82,69 @@ pub enum Primitive { Repeat(Id, Vec3, i32), } +impl std::fmt::Debug for Primitive { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + Primitive::Empty => f.debug_tuple("Empty").finish(), + Primitive::Aabb(aabb) => f.debug_tuple("Aabb").field(&aabb).finish(), + Primitive::Pyramid { aabb, inset } => { + f.debug_tuple("Pyramid").field(&aabb).field(&inset).finish() + }, + Primitive::Ramp { aabb, inset, dir } => { + f.debug_tuple("Ramp").field(&aabb).field(&inset).field(&dir).finish() + }, + Primitive::Gable { aabb, inset, dir } => { + f.debug_tuple("Gable").field(&aabb).field(&inset).field(&dir).finish() + }, + Primitive::Cylinder(aabb) => f.debug_tuple("Cylinder").field(&aabb).finish(), + Primitive::Cone(aabb) => f.debug_tuple("Cone").field(&aabb).finish(), + Primitive::Sphere(aabb) => f.debug_tuple("Sphere").field(&aabb).finish(), + Primitive::Superquadric { aabb, degree } => f + .debug_tuple("Superquadric") + .field(&aabb) + .field(°ree) + .finish(), + Primitive::Plane(aabr, origin, gradient) => f + .debug_tuple("Plane") + .field(&aabr) + .field(&origin) + .field(&gradient) + .finish(), + Primitive::Segment { segment, radius } => f + .debug_tuple("Segment") + .field(&segment) + .field(&radius) + .finish(), + Primitive::SegmentPrism { + segment, + radius, + height, + } => f + .debug_tuple("SegmentPrism") + .field(&segment) + .field(&radius) + .field(&height) + .finish(), + Primitive::Sampling(prim, _) => f.debug_tuple("Sampling").field(&prim).finish(), + Primitive::Prefab(prefab) => f.debug_tuple("Prefab").field(&prefab).finish(), + Primitive::Intersect(a, b) => f.debug_tuple("Intersect").field(&a).field(&b).finish(), + Primitive::Union(a, b) => f.debug_tuple("Union").field(&a).field(&b).finish(), + Primitive::Without(a, b) => f.debug_tuple("Without").field(&a).field(&b).finish(), + Primitive::Rotate(a, mat) => f.debug_tuple("Rotate").field(&a).field(&mat).finish(), + Primitive::Translate(a, vec) => { + f.debug_tuple("Translate").field(&a).field(&vec).finish() + }, + Primitive::Scale(a, vec) => f.debug_tuple("Scale").field(&a).field(&vec).finish(), + Primitive::Repeat(a, offset, n) => f + .debug_tuple("Repeat") + .field(&a) + .field(&offset) + .field(&n) + .finish(), + } + } +} + impl Primitive { pub fn intersect(a: impl Into>, b: impl Into>) -> Self { Self::Intersect(a.into(), b.into()) @@ -387,7 +450,7 @@ impl Fill { } } - fn get_bounds_inner(&self, tree: &Store, prim: Id) -> Option> { + fn get_bounds_inner(&self, tree: &Store, prim: Id) -> Vec> { fn or_zip_with T>(a: Option, b: Option, f: F) -> Option { match (a, b) { (Some(a), Some(b)) => Some(f(a, b)), @@ -396,16 +459,16 @@ impl Fill { } } - Some(match &tree[prim] { - Primitive::Empty => return None, - Primitive::Aabb(aabb) => *aabb, - Primitive::Pyramid { aabb, .. } => *aabb, - Primitive::Gable { aabb, .. } => *aabb, - Primitive::Ramp { aabb, .. } => *aabb, - Primitive::Cylinder(aabb) => *aabb, - Primitive::Cone(aabb) => *aabb, - Primitive::Sphere(aabb) => *aabb, - Primitive::Superquadric { aabb, .. } => *aabb, + match &tree[prim] { + Primitive::Empty => vec![], + Primitive::Aabb(aabb) => vec![*aabb], + Primitive::Pyramid { aabb, .. } => vec![*aabb], + Primitive::Gable { aabb, .. } => vec![*aabb], + Primitive::Ramp { aabb, .. } => vec![*aabb], + Primitive::Cylinder(aabb) => vec![*aabb], + Primitive::Cone(aabb) => vec![*aabb], + Primitive::Sphere(aabb) => vec![*aabb], + Primitive::Superquadric { aabb, .. } => vec![*aabb], Primitive::Plane(aabr, origin, gradient) => { let half_size = aabr.half_size().reduce_max(); let longest_dist = ((aabr.center() - origin.xy()).map(|x| x.abs()) @@ -421,7 +484,7 @@ impl Fill { min: aabr.min.with_z(origin.z + z.reduce_min().min(0)), max: aabr.max.with_z(origin.z + z.reduce_max().max(0)), }; - aabb.made_valid() + vec![aabb.made_valid()] }, Primitive::Segment { segment, radius } => { let aabb = Aabb { @@ -429,10 +492,10 @@ impl Fill { max: segment.end, } .made_valid(); - Aabb { + vec![Aabb { min: (aabb.min - *radius).floor().as_(), max: (aabb.max + *radius).ceil().as_(), - } + }] }, Primitive::SegmentPrism { segment, @@ -452,58 +515,108 @@ impl Fill { let xy = (aabb.max.xy() + *radius).ceil(); xy.with_z((aabb.max.z + *height).ceil()).as_() }; - Aabb { min, max } + vec![Aabb { min, max }] }, - Primitive::Sampling(a, _) => self.get_bounds_inner(tree, *a)?, - Primitive::Prefab(p) => p.get_bounds(), + Primitive::Sampling(a, _) => self.get_bounds_inner(tree, *a), + Primitive::Prefab(p) => vec![p.get_bounds()], Primitive::Intersect(a, b) => or_zip_with( - self.get_bounds_inner(tree, *a), - self.get_bounds_inner(tree, *b), + self.get_bounds_opt(tree, *a), + self.get_bounds_opt(tree, *b), |a, b| a.intersection(b), - )?, - Primitive::Union(a, b) => or_zip_with( - self.get_bounds_inner(tree, *a), - self.get_bounds_inner(tree, *b), - |a, b| a.union(b), - )?, - Primitive::Without(a, _) => self.get_bounds_inner(tree, *a)?, - Primitive::Rotate(prim, mat) => { - let aabb = self.get_bounds_inner(tree, *prim)?; - let extent = *mat * Vec3::from(aabb.size()); - let new_aabb: Aabb = Aabb { - min: aabb.min, - max: aabb.min + extent, - }; - new_aabb.made_valid() + ).into_iter().collect(), + + Primitive::Union(a, b) => { + fn jaccard(x: Aabb, y: Aabb) -> f32 { + let s_intersection = x.intersection(y).size().as_::().magnitude(); + let s_union = x.union(y).size().as_::().magnitude(); + s_intersection / s_union + } + let mut inputs = Vec::new(); + inputs.extend(self.get_bounds_inner(tree, *a)); + inputs.extend(self.get_bounds_inner(tree, *b)); + let mut results = Vec::new(); + if let Some(aabb) = inputs.pop() { + results.push(aabb); + for a in &inputs { + let best = results.iter().enumerate().max_by_key(|(_, b)| (jaccard(*a, **b) * 1000.0) as usize); + match best { + Some((i, b)) if jaccard(*a, *b) > 0.3 => { + let mut aabb = results.swap_remove(i); + aabb = aabb.union(*a); + results.push(aabb); + }, + _ => results.push(*a), + } + } + results + } else { + results + } }, - Primitive::Translate(prim, vec) => { - let aabb = self.get_bounds_inner(tree, *prim)?; - Aabb { + Primitive::Without(a, _) => self.get_bounds_inner(tree, *a), + Primitive::Rotate(prim, mat) => self + .get_bounds_inner(tree, *prim) + .into_iter() + .map(|aabb| { + let extent = *mat * Vec3::from(aabb.size()); + let new_aabb: Aabb = Aabb { + min: aabb.min, + max: aabb.min + extent, + }; + new_aabb.made_valid() + }) + .collect(), + Primitive::Translate(prim, vec) => self + .get_bounds_inner(tree, *prim) + .into_iter() + .map(|aabb| Aabb { min: aabb.min.map2(*vec, i32::saturating_add), max: aabb.max.map2(*vec, i32::saturating_add), - } - }, - Primitive::Scale(prim, vec) => { - let aabb = self.get_bounds_inner(tree, *prim)?; - let center = aabb.center(); - Aabb { - min: center + ((aabb.min - center).as_::() * vec).as_::(), - max: center + ((aabb.max - center).as_::() * vec).as_::(), - } - }, - Primitive::Repeat(prim, offset, count) => { - let aabb = self.get_bounds_inner(tree, *prim)?; - Aabb { + }) + .collect(), + Primitive::Scale(prim, vec) => self + .get_bounds_inner(tree, *prim) + .into_iter() + .map(|aabb| { + let center = aabb.center(); + Aabb { + min: center + ((aabb.min - center).as_::() * vec).as_::(), + max: center + ((aabb.max - center).as_::() * vec).as_::(), + } + }) + .collect(), + Primitive::Repeat(prim, offset, count) => self + .get_bounds_inner(tree, *prim) + .into_iter() + .map(|aabb| Aabb { min: aabb.min.map2(aabb.min + offset * count, |a, b| a.min(b)), max: aabb.max.map2(aabb.max + offset * count, |a, b| a.max(b)), - } - }, - }) + }) + .collect(), + } + } + + pub fn get_bounds_disjoint( + &self, + tree: &Store, + prim: Id, + ) -> Vec> { + self.get_bounds_inner(tree, prim) + } + + pub fn get_bounds_opt( + &self, + tree: &Store, + prim: Id, + ) -> Option> { + self.get_bounds_inner(tree, prim) + .into_iter() + .reduce(|a, b| a.union(b)) } pub fn get_bounds(&self, tree: &Store, prim: Id) -> Aabb { - self.get_bounds_inner(tree, prim) - .unwrap_or_else(|| Aabb::new_empty(Vec3::zero())) + self.get_bounds_opt(tree, prim) + .unwrap_or(Aabb::new_empty(Vec3::zero())) } } diff --git a/world/src/site2/mod.rs b/world/src/site2/mod.rs index 7bec90181f..7a6466b53c 100644 --- a/world/src/site2/mod.rs +++ b/world/src/site2/mod.rs @@ -983,31 +983,32 @@ impl Site { }; for (prim, fill) in fills { - let mut aabb = fill.get_bounds(&prim_tree, prim); - aabb.min = Vec2::max(aabb.min.xy(), chunk_aabr.min).with_z(aabb.min.z); - aabb.max = Vec2::min(aabb.max.xy(), chunk_aabr.max).with_z(aabb.max.z); + for mut aabb in fill.get_bounds_disjoint(&prim_tree, prim) { + aabb.min = Vec2::max(aabb.min.xy(), chunk_aabr.min).with_z(aabb.min.z); + aabb.max = Vec2::min(aabb.max.xy(), chunk_aabr.max).with_z(aabb.max.z); - for x in aabb.min.x..aabb.max.x { - for y in aabb.min.y..aabb.max.y { - let col_tile = self.wpos_tile(Vec2::new(x, y)); - if - /* col_tile.is_building() && */ - col_tile - .plot - .and_then(|p| self.plots[p].z_range()) - .zip(self.plots[plot].z_range()) - .map_or(false, |(a, b)| a.end > b.end) - { - continue; - } + for x in aabb.min.x..aabb.max.x { + for y in aabb.min.y..aabb.max.y { + let col_tile = self.wpos_tile(Vec2::new(x, y)); + if + /* col_tile.is_building() && */ + col_tile + .plot + .and_then(|p| self.plots[p].z_range()) + .zip(self.plots[plot].z_range()) + .map_or(false, |(a, b)| a.end > b.end) + { + continue; + } - for z in aabb.min.z..aabb.max.z { - let pos = Vec3::new(x, y, z); + for z in aabb.min.z..aabb.max.z { + let pos = Vec3::new(x, y, z); - canvas.map(pos, |block| { - fill.sample_at(&prim_tree, prim, pos, &info, block) - .unwrap_or(block) - }); + canvas.map(pos, |block| { + fill.sample_at(&prim_tree, prim, pos, &info, block) + .unwrap_or(block) + }); + } } } }