2021-03-02 14:07:30 +00:00
|
|
|
use super::*;
|
2021-03-05 13:53:55 +00:00
|
|
|
use crate::util::{RandomField, Sampler};
|
2021-02-28 23:34:36 +00:00
|
|
|
use common::{
|
|
|
|
store::{Id, Store},
|
2021-03-05 13:53:55 +00:00
|
|
|
terrain::{Block, BlockKind},
|
2021-02-28 23:34:36 +00:00
|
|
|
};
|
|
|
|
use vek::*;
|
|
|
|
|
|
|
|
pub enum Primitive {
|
|
|
|
Empty, // Placeholder
|
2021-02-28 23:59:04 +00:00
|
|
|
|
|
|
|
// Shapes
|
2021-02-28 23:34:36 +00:00
|
|
|
Aabb(Aabb<i32>),
|
2021-02-28 23:59:04 +00:00
|
|
|
Pyramid { aabb: Aabb<i32>, inset: i32 },
|
2021-03-17 22:31:07 +00:00
|
|
|
Cylinder(Aabb<i32>),
|
|
|
|
Cone(Aabb<i32>),
|
|
|
|
Sphere(Aabb<i32>),
|
|
|
|
Plane(Aabr<i32>, Vec3<i32>, Vec2<f32>),
|
2021-02-28 23:59:04 +00:00
|
|
|
|
|
|
|
// Combinators
|
2021-02-28 23:34:36 +00:00
|
|
|
And(Id<Primitive>, Id<Primitive>),
|
|
|
|
Or(Id<Primitive>, Id<Primitive>),
|
|
|
|
Xor(Id<Primitive>, Id<Primitive>),
|
2021-03-17 22:31:07 +00:00
|
|
|
// Not commutative
|
|
|
|
Diff(Id<Primitive>, Id<Primitive>),
|
|
|
|
// Operators
|
2021-02-28 23:34:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-05 13:08:50 +00:00
|
|
|
pub enum Fill {
|
|
|
|
Block(Block),
|
2021-03-05 13:53:55 +00:00
|
|
|
Brick(BlockKind, Rgb<u8>, u8),
|
2021-02-28 23:34:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Fill {
|
|
|
|
fn contains_at(&self, tree: &Store<Primitive>, prim: Id<Primitive>, pos: Vec3<i32>) -> bool {
|
2021-02-28 23:59:04 +00:00
|
|
|
// Custom closure because vek's impl of `contains_point` is inclusive :(
|
2021-03-05 01:23:11 +00:00
|
|
|
let aabb_contains = |aabb: Aabb<i32>, pos: Vec3<i32>| {
|
|
|
|
(aabb.min.x..aabb.max.x).contains(&pos.x)
|
|
|
|
&& (aabb.min.y..aabb.max.y).contains(&pos.y)
|
|
|
|
&& (aabb.min.z..aabb.max.z).contains(&pos.z)
|
|
|
|
};
|
2021-02-28 23:59:04 +00:00
|
|
|
|
2021-02-28 23:34:36 +00:00
|
|
|
match &tree[prim] {
|
|
|
|
Primitive::Empty => false,
|
2021-02-28 23:59:04 +00:00
|
|
|
|
|
|
|
Primitive::Aabb(aabb) => aabb_contains(*aabb, pos),
|
|
|
|
Primitive::Pyramid { aabb, inset } => {
|
2021-03-01 21:14:38 +00:00
|
|
|
let inset = (*inset).max(aabb.size().reduce_min());
|
2021-03-05 01:23:11 +00:00
|
|
|
let inner = Aabr {
|
|
|
|
min: aabb.min.xy() - 1 + inset,
|
|
|
|
max: aabb.max.xy() - inset,
|
|
|
|
};
|
|
|
|
aabb_contains(*aabb, pos)
|
|
|
|
&& (inner.projected_point(pos.xy()) - pos.xy())
|
|
|
|
.map(|e| e.abs())
|
|
|
|
.reduce_max() as f32
|
|
|
|
/ (inset as f32)
|
|
|
|
< 1.0
|
|
|
|
- ((pos.z - aabb.min.z) as f32 + 0.5) / (aabb.max.z - aabb.min.z) as f32
|
2021-02-28 23:59:04 +00:00
|
|
|
},
|
2021-03-17 22:31:07 +00:00
|
|
|
Primitive::Cylinder(aabb) => {
|
|
|
|
aabb_contains(*aabb, pos)
|
|
|
|
&& (pos
|
|
|
|
.xy()
|
|
|
|
.as_()
|
|
|
|
.distance_squared(aabb.as_().center().xy() - 0.5)
|
|
|
|
as f32)
|
|
|
|
< (aabb.size().w.min(aabb.size().h) as f32 / 2.0).powi(2)
|
|
|
|
},
|
|
|
|
Primitive::Cone(aabb) => {
|
|
|
|
aabb_contains(*aabb, pos)
|
|
|
|
&& pos
|
|
|
|
.xy()
|
|
|
|
.as_()
|
|
|
|
.distance_squared(aabb.as_().center().xy() - 0.5)
|
|
|
|
< (((aabb.max.z - pos.z) as f32 / aabb.size().d as f32)
|
|
|
|
* (aabb.size().w.min(aabb.size().h) as f32 / 2.0))
|
|
|
|
.powi(2)
|
|
|
|
},
|
|
|
|
Primitive::Sphere(aabb) => {
|
|
|
|
aabb_contains(*aabb, pos)
|
|
|
|
&& pos.as_().distance_squared(aabb.as_().center() - 0.5)
|
|
|
|
< (aabb.size().w.min(aabb.size().h) as f32 / 2.0).powi(2)
|
|
|
|
},
|
|
|
|
Primitive::Plane(aabr, origin, gradient) => {
|
|
|
|
// Maybe <= instead of ==
|
|
|
|
pos.z
|
|
|
|
== origin.z
|
|
|
|
+ ((pos.xy() - origin.xy())
|
|
|
|
.map(|x| x.abs())
|
|
|
|
.as_()
|
|
|
|
.dot(*gradient) as i32)
|
|
|
|
},
|
2021-03-05 01:23:11 +00:00
|
|
|
Primitive::And(a, b) => {
|
|
|
|
self.contains_at(tree, *a, pos) && self.contains_at(tree, *b, pos)
|
|
|
|
},
|
|
|
|
Primitive::Or(a, b) => {
|
|
|
|
self.contains_at(tree, *a, pos) || self.contains_at(tree, *b, pos)
|
|
|
|
},
|
|
|
|
Primitive::Xor(a, b) => {
|
|
|
|
self.contains_at(tree, *a, pos) ^ self.contains_at(tree, *b, pos)
|
|
|
|
},
|
2021-03-17 22:31:07 +00:00
|
|
|
Primitive::Diff(a, b) => {
|
|
|
|
self.contains_at(tree, *a, pos) && !self.contains_at(tree, *b, pos)
|
|
|
|
},
|
2021-02-28 23:34:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-05 13:08:50 +00:00
|
|
|
pub fn sample_at(
|
|
|
|
&self,
|
|
|
|
tree: &Store<Primitive>,
|
|
|
|
prim: Id<Primitive>,
|
|
|
|
pos: Vec3<i32>,
|
|
|
|
) -> Option<Block> {
|
|
|
|
if self.contains_at(tree, prim, pos) {
|
|
|
|
match self {
|
|
|
|
Fill::Block(block) => Some(*block),
|
2021-03-05 13:53:55 +00:00
|
|
|
Fill::Brick(bk, col, range) => Some(Block::new(
|
|
|
|
*bk,
|
|
|
|
*col + (RandomField::new(13)
|
|
|
|
.get((pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1))
|
|
|
|
% *range as u32) as u8,
|
|
|
|
)),
|
2021-03-05 13:08:50 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
2021-02-28 23:34:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-03 13:31:25 +00:00
|
|
|
fn get_bounds_inner(&self, tree: &Store<Primitive>, prim: Id<Primitive>) -> Option<Aabb<i32>> {
|
|
|
|
fn or_zip_with<T, F: FnOnce(T, T) -> T>(a: Option<T>, b: Option<T>, f: F) -> Option<T> {
|
|
|
|
match (a, b) {
|
|
|
|
(Some(a), Some(b)) => Some(f(a, b)),
|
|
|
|
(Some(a), _) => Some(a),
|
|
|
|
(_, b) => b,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Some(match &tree[prim] {
|
|
|
|
Primitive::Empty => return None,
|
2021-02-28 23:34:36 +00:00
|
|
|
Primitive::Aabb(aabb) => *aabb,
|
2021-02-28 23:59:04 +00:00
|
|
|
Primitive::Pyramid { aabb, .. } => *aabb,
|
2021-03-17 22:31:07 +00:00
|
|
|
Primitive::Cylinder(aabb) => *aabb,
|
|
|
|
Primitive::Cone(aabb) => *aabb,
|
|
|
|
Primitive::Sphere(aabb) => *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())
|
|
|
|
+ half_size
|
|
|
|
+ aabr.size().reduce_max() % 2)
|
|
|
|
.map(|x| x as f32);
|
|
|
|
let z = if gradient.x.signum() == gradient.y.signum() {
|
|
|
|
Vec2::new(0, longest_dist.dot(*gradient) as i32)
|
|
|
|
} else {
|
|
|
|
(longest_dist * gradient).as_()
|
|
|
|
};
|
|
|
|
Aabb {
|
|
|
|
min: aabr.min.with_z(origin.z + z.reduce_min().min(0)),
|
|
|
|
max: aabr.max.with_z(origin.z + z.reduce_max().max(0)),
|
|
|
|
}
|
|
|
|
},
|
2021-03-05 01:23:11 +00:00
|
|
|
Primitive::And(a, b) => or_zip_with(
|
|
|
|
self.get_bounds_inner(tree, *a),
|
|
|
|
self.get_bounds_inner(tree, *b),
|
|
|
|
|a, b| a.intersection(b),
|
|
|
|
)?,
|
|
|
|
Primitive::Or(a, b) | Primitive::Xor(a, b) => or_zip_with(
|
|
|
|
self.get_bounds_inner(tree, *a),
|
|
|
|
self.get_bounds_inner(tree, *b),
|
|
|
|
|a, b| a.union(b),
|
|
|
|
)?,
|
2021-03-17 22:31:07 +00:00
|
|
|
Primitive::Diff(a, _) => self.get_bounds_inner(tree, *a)?,
|
2021-03-03 13:31:25 +00:00
|
|
|
})
|
2021-02-28 23:34:36 +00:00
|
|
|
}
|
|
|
|
|
2021-03-05 13:08:50 +00:00
|
|
|
pub fn get_bounds(&self, tree: &Store<Primitive>, prim: Id<Primitive>) -> Aabb<i32> {
|
|
|
|
self.get_bounds_inner(tree, prim)
|
2021-03-05 01:23:11 +00:00
|
|
|
.unwrap_or_else(|| Aabb::new_empty(Vec3::zero()))
|
2021-02-28 23:34:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub trait Structure {
|
2021-03-05 13:08:50 +00:00
|
|
|
fn render<F: FnMut(Primitive) -> Id<Primitive>, G: FnMut(Id<Primitive>, Fill)>(
|
2021-02-28 23:34:36 +00:00
|
|
|
&self,
|
2021-03-02 14:07:30 +00:00
|
|
|
site: &Site,
|
2021-03-01 23:43:26 +00:00
|
|
|
prim: F,
|
|
|
|
fill: G,
|
2021-03-05 13:08:50 +00:00
|
|
|
);
|
2021-02-28 23:34:36 +00:00
|
|
|
|
|
|
|
// Generate a primitive tree and fills for this structure
|
2021-03-05 13:08:50 +00:00
|
|
|
fn render_collect(&self, site: &Site) -> (Store<Primitive>, Vec<(Id<Primitive>, Fill)>) {
|
2021-02-28 23:34:36 +00:00
|
|
|
let mut tree = Store::default();
|
|
|
|
let mut fills = Vec::new();
|
2021-03-06 17:23:03 +00:00
|
|
|
self.render(site, |p| tree.insert(p), |p, f| fills.push((p, f)));
|
2021-02-28 23:34:36 +00:00
|
|
|
(tree, fills)
|
|
|
|
}
|
|
|
|
}
|