mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Iterate over partially-disjoint unions of primitive bounds, improving CSG rasterization performance in the presence of sparse/disjoint unions.
This commit is contained in:
parent
3373985a5f
commit
aab552afc5
@ -82,6 +82,69 @@ pub enum Primitive {
|
|||||||
Repeat(Id<Primitive>, Vec3<i32>, i32),
|
Repeat(Id<Primitive>, Vec3<i32>, 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 {
|
impl Primitive {
|
||||||
pub fn intersect(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
|
pub fn intersect(a: impl Into<Id<Primitive>>, b: impl Into<Id<Primitive>>) -> Self {
|
||||||
Self::Intersect(a.into(), b.into())
|
Self::Intersect(a.into(), b.into())
|
||||||
@ -387,7 +450,7 @@ impl Fill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bounds_inner(&self, tree: &Store<Primitive>, prim: Id<Primitive>) -> Option<Aabb<i32>> {
|
fn get_bounds_inner(&self, tree: &Store<Primitive>, prim: Id<Primitive>) -> Vec<Aabb<i32>> {
|
||||||
fn or_zip_with<T, F: FnOnce(T, T) -> T>(a: Option<T>, b: Option<T>, f: F) -> Option<T> {
|
fn or_zip_with<T, F: FnOnce(T, T) -> T>(a: Option<T>, b: Option<T>, f: F) -> Option<T> {
|
||||||
match (a, b) {
|
match (a, b) {
|
||||||
(Some(a), Some(b)) => Some(f(a, b)),
|
(Some(a), Some(b)) => Some(f(a, b)),
|
||||||
@ -396,16 +459,16 @@ impl Fill {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(match &tree[prim] {
|
match &tree[prim] {
|
||||||
Primitive::Empty => return None,
|
Primitive::Empty => vec![],
|
||||||
Primitive::Aabb(aabb) => *aabb,
|
Primitive::Aabb(aabb) => vec![*aabb],
|
||||||
Primitive::Pyramid { aabb, .. } => *aabb,
|
Primitive::Pyramid { aabb, .. } => vec![*aabb],
|
||||||
Primitive::Gable { aabb, .. } => *aabb,
|
Primitive::Gable { aabb, .. } => vec![*aabb],
|
||||||
Primitive::Ramp { aabb, .. } => *aabb,
|
Primitive::Ramp { aabb, .. } => vec![*aabb],
|
||||||
Primitive::Cylinder(aabb) => *aabb,
|
Primitive::Cylinder(aabb) => vec![*aabb],
|
||||||
Primitive::Cone(aabb) => *aabb,
|
Primitive::Cone(aabb) => vec![*aabb],
|
||||||
Primitive::Sphere(aabb) => *aabb,
|
Primitive::Sphere(aabb) => vec![*aabb],
|
||||||
Primitive::Superquadric { aabb, .. } => *aabb,
|
Primitive::Superquadric { aabb, .. } => vec![*aabb],
|
||||||
Primitive::Plane(aabr, origin, gradient) => {
|
Primitive::Plane(aabr, origin, gradient) => {
|
||||||
let half_size = aabr.half_size().reduce_max();
|
let half_size = aabr.half_size().reduce_max();
|
||||||
let longest_dist = ((aabr.center() - origin.xy()).map(|x| x.abs())
|
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)),
|
min: aabr.min.with_z(origin.z + z.reduce_min().min(0)),
|
||||||
max: aabr.max.with_z(origin.z + z.reduce_max().max(0)),
|
max: aabr.max.with_z(origin.z + z.reduce_max().max(0)),
|
||||||
};
|
};
|
||||||
aabb.made_valid()
|
vec![aabb.made_valid()]
|
||||||
},
|
},
|
||||||
Primitive::Segment { segment, radius } => {
|
Primitive::Segment { segment, radius } => {
|
||||||
let aabb = Aabb {
|
let aabb = Aabb {
|
||||||
@ -429,10 +492,10 @@ impl Fill {
|
|||||||
max: segment.end,
|
max: segment.end,
|
||||||
}
|
}
|
||||||
.made_valid();
|
.made_valid();
|
||||||
Aabb {
|
vec![Aabb {
|
||||||
min: (aabb.min - *radius).floor().as_(),
|
min: (aabb.min - *radius).floor().as_(),
|
||||||
max: (aabb.max + *radius).ceil().as_(),
|
max: (aabb.max + *radius).ceil().as_(),
|
||||||
}
|
}]
|
||||||
},
|
},
|
||||||
Primitive::SegmentPrism {
|
Primitive::SegmentPrism {
|
||||||
segment,
|
segment,
|
||||||
@ -452,58 +515,108 @@ impl Fill {
|
|||||||
let xy = (aabb.max.xy() + *radius).ceil();
|
let xy = (aabb.max.xy() + *radius).ceil();
|
||||||
xy.with_z((aabb.max.z + *height).ceil()).as_()
|
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::Sampling(a, _) => self.get_bounds_inner(tree, *a),
|
||||||
Primitive::Prefab(p) => p.get_bounds(),
|
Primitive::Prefab(p) => vec![p.get_bounds()],
|
||||||
Primitive::Intersect(a, b) => or_zip_with(
|
Primitive::Intersect(a, b) => or_zip_with(
|
||||||
self.get_bounds_inner(tree, *a),
|
self.get_bounds_opt(tree, *a),
|
||||||
self.get_bounds_inner(tree, *b),
|
self.get_bounds_opt(tree, *b),
|
||||||
|a, b| a.intersection(b),
|
|a, b| a.intersection(b),
|
||||||
)?,
|
).into_iter().collect(),
|
||||||
Primitive::Union(a, b) => or_zip_with(
|
|
||||||
self.get_bounds_inner(tree, *a),
|
Primitive::Union(a, b) => {
|
||||||
self.get_bounds_inner(tree, *b),
|
fn jaccard(x: Aabb<i32>, y: Aabb<i32>) -> f32 {
|
||||||
|a, b| a.union(b),
|
let s_intersection = x.intersection(y).size().as_::<f32>().magnitude();
|
||||||
)?,
|
let s_union = x.union(y).size().as_::<f32>().magnitude();
|
||||||
Primitive::Without(a, _) => self.get_bounds_inner(tree, *a)?,
|
s_intersection / s_union
|
||||||
Primitive::Rotate(prim, mat) => {
|
}
|
||||||
let aabb = self.get_bounds_inner(tree, *prim)?;
|
let mut inputs = Vec::new();
|
||||||
let extent = *mat * Vec3::from(aabb.size());
|
inputs.extend(self.get_bounds_inner(tree, *a));
|
||||||
let new_aabb: Aabb<i32> = Aabb {
|
inputs.extend(self.get_bounds_inner(tree, *b));
|
||||||
min: aabb.min,
|
let mut results = Vec::new();
|
||||||
max: aabb.min + extent,
|
if let Some(aabb) = inputs.pop() {
|
||||||
};
|
results.push(aabb);
|
||||||
new_aabb.made_valid()
|
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) => {
|
Primitive::Without(a, _) => self.get_bounds_inner(tree, *a),
|
||||||
let aabb = self.get_bounds_inner(tree, *prim)?;
|
Primitive::Rotate(prim, mat) => self
|
||||||
Aabb {
|
.get_bounds_inner(tree, *prim)
|
||||||
|
.into_iter()
|
||||||
|
.map(|aabb| {
|
||||||
|
let extent = *mat * Vec3::from(aabb.size());
|
||||||
|
let new_aabb: Aabb<i32> = 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),
|
min: aabb.min.map2(*vec, i32::saturating_add),
|
||||||
max: aabb.max.map2(*vec, i32::saturating_add),
|
max: aabb.max.map2(*vec, i32::saturating_add),
|
||||||
}
|
})
|
||||||
},
|
.collect(),
|
||||||
Primitive::Scale(prim, vec) => {
|
Primitive::Scale(prim, vec) => self
|
||||||
let aabb = self.get_bounds_inner(tree, *prim)?;
|
.get_bounds_inner(tree, *prim)
|
||||||
let center = aabb.center();
|
.into_iter()
|
||||||
Aabb {
|
.map(|aabb| {
|
||||||
min: center + ((aabb.min - center).as_::<f32>() * vec).as_::<i32>(),
|
let center = aabb.center();
|
||||||
max: center + ((aabb.max - center).as_::<f32>() * vec).as_::<i32>(),
|
Aabb {
|
||||||
}
|
min: center + ((aabb.min - center).as_::<f32>() * vec).as_::<i32>(),
|
||||||
},
|
max: center + ((aabb.max - center).as_::<f32>() * vec).as_::<i32>(),
|
||||||
Primitive::Repeat(prim, offset, count) => {
|
}
|
||||||
let aabb = self.get_bounds_inner(tree, *prim)?;
|
})
|
||||||
Aabb {
|
.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)),
|
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)),
|
max: aabb.max.map2(aabb.max + offset * count, |a, b| a.max(b)),
|
||||||
}
|
})
|
||||||
},
|
.collect(),
|
||||||
})
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bounds_disjoint(
|
||||||
|
&self,
|
||||||
|
tree: &Store<Primitive>,
|
||||||
|
prim: Id<Primitive>,
|
||||||
|
) -> Vec<Aabb<i32>> {
|
||||||
|
self.get_bounds_inner(tree, prim)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_bounds_opt(
|
||||||
|
&self,
|
||||||
|
tree: &Store<Primitive>,
|
||||||
|
prim: Id<Primitive>,
|
||||||
|
) -> Option<Aabb<i32>> {
|
||||||
|
self.get_bounds_inner(tree, prim)
|
||||||
|
.into_iter()
|
||||||
|
.reduce(|a, b| a.union(b))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bounds(&self, tree: &Store<Primitive>, prim: Id<Primitive>) -> Aabb<i32> {
|
pub fn get_bounds(&self, tree: &Store<Primitive>, prim: Id<Primitive>) -> Aabb<i32> {
|
||||||
self.get_bounds_inner(tree, prim)
|
self.get_bounds_opt(tree, prim)
|
||||||
.unwrap_or_else(|| Aabb::new_empty(Vec3::zero()))
|
.unwrap_or(Aabb::new_empty(Vec3::zero()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -983,31 +983,32 @@ impl Site {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (prim, fill) in fills {
|
for (prim, fill) in fills {
|
||||||
let mut aabb = fill.get_bounds(&prim_tree, prim);
|
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.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);
|
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 x in aabb.min.x..aabb.max.x {
|
||||||
for y in aabb.min.y..aabb.max.y {
|
for y in aabb.min.y..aabb.max.y {
|
||||||
let col_tile = self.wpos_tile(Vec2::new(x, y));
|
let col_tile = self.wpos_tile(Vec2::new(x, y));
|
||||||
if
|
if
|
||||||
/* col_tile.is_building() && */
|
/* col_tile.is_building() && */
|
||||||
col_tile
|
col_tile
|
||||||
.plot
|
.plot
|
||||||
.and_then(|p| self.plots[p].z_range())
|
.and_then(|p| self.plots[p].z_range())
|
||||||
.zip(self.plots[plot].z_range())
|
.zip(self.plots[plot].z_range())
|
||||||
.map_or(false, |(a, b)| a.end > b.end)
|
.map_or(false, |(a, b)| a.end > b.end)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for z in aabb.min.z..aabb.max.z {
|
for z in aabb.min.z..aabb.max.z {
|
||||||
let pos = Vec3::new(x, y, z);
|
let pos = Vec3::new(x, y, z);
|
||||||
|
|
||||||
canvas.map(pos, |block| {
|
canvas.map(pos, |block| {
|
||||||
fill.sample_at(&prim_tree, prim, pos, &info, block)
|
fill.sample_at(&prim_tree, prim, pos, &info, block)
|
||||||
.unwrap_or(block)
|
.unwrap_or(block)
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user