Site2 util

This commit is contained in:
Isidor Nielsen 2022-01-15 13:06:18 +00:00 committed by Dominik Broński
parent 4b1900d3ea
commit 5642680687
5 changed files with 275 additions and 34 deletions

View File

@ -1,6 +1,7 @@
use super::*;
use crate::{
block::block_from_structure,
site2::util::Dir,
util::{RandomField, Sampler},
};
use common::{
@ -27,13 +28,13 @@ pub enum Primitive {
Ramp {
aabb: Aabb<i32>,
inset: i32,
dir: u8,
dir: Dir,
},
Gable {
aabb: Aabb<i32>,
inset: i32,
// X axis parallel or Y axis parallel
dir: bool,
dir: Dir,
},
Cylinder(Aabb<i32>),
Cone(Aabb<i32>),
@ -92,6 +93,7 @@ pub enum Fill {
Sprite(SpriteKind),
Block(Block),
Brick(BlockKind, Rgb<u8>, u8),
Gradient(util::gradient::Gradient, BlockKind),
// TODO: the offset field for Prefab is a hack that breaks the compositionality of Translate,
// we probably need an evaluator for the primitive tree that gets which point is queried at
// leaf nodes given an input point to make Translate/Rotate work generally
@ -115,19 +117,19 @@ impl Fill {
Primitive::Ramp { aabb, inset, dir } => {
let inset = (*inset).max(aabb.size().reduce_min());
let inner = match dir {
0 => Aabr {
Dir::X => Aabr {
min: Vec2::new(aabb.min.x - 1 + inset, aabb.min.y),
max: Vec2::new(aabb.max.x, aabb.max.y),
},
1 => Aabr {
Dir::NegX => Aabr {
min: Vec2::new(aabb.min.x, aabb.min.y),
max: Vec2::new(aabb.max.x - inset, aabb.max.y),
},
2 => Aabr {
Dir::Y => Aabr {
min: Vec2::new(aabb.min.x, aabb.min.y - 1 + inset),
max: Vec2::new(aabb.max.x, aabb.max.y),
},
_ => Aabr {
Dir::NegY => Aabr {
min: Vec2::new(aabb.min.x, aabb.min.y),
max: Vec2::new(aabb.max.x, aabb.max.y - inset),
},
@ -156,7 +158,7 @@ impl Fill {
},
Primitive::Gable { aabb, inset, dir } => {
let inset = (*inset).max(aabb.size().reduce_min());
let inner = if *dir {
let inner = if dir.is_y() {
Aabr {
min: Vec2::new(aabb.min.x - 1 + inset, aabb.min.y),
max: Vec2::new(aabb.max.x - inset, aabb.max.y),
@ -270,6 +272,7 @@ impl Fill {
.get((pos + Vec3::new(pos.z, pos.z, 0)) / Vec3::new(2, 2, 1))
% *range as u32) as u8,
)),
Fill::Gradient(gradient, bk) => Some(Block::new(*bk, gradient.sample(pos.as_()))),
Fill::Prefab(p, tr, seed) => p.get(pos - tr).ok().and_then(|sb| {
let col_sample = canvas_info.col(canvas_info.wpos)?;
block_from_structure(
@ -403,22 +406,22 @@ impl Painter {
self.prim(Primitive::Ramp {
aabb,
inset,
dir: 0,
dir: Dir::X,
})
.intersect(self.prim(Primitive::Ramp {
aabb,
inset,
dir: 1,
dir: Dir::NegX,
}))
.intersect(self.prim(Primitive::Ramp {
aabb,
inset,
dir: 2,
dir: Dir::Y,
}))
.intersect(self.prim(Primitive::Ramp {
aabb,
inset,
dir: 3,
dir: Dir::NegY,
}))
}
@ -460,6 +463,11 @@ impl<'a> PrimitiveRef<'a> {
pub fn fill(self, fill: Fill) { self.painter.fill(self, fill); }
pub fn clear(self) { self.painter.fill(self, Fill::Block(Block::empty())); }
pub fn sample(self, sampling: impl Fn(Vec3<i32>) -> bool + 'static) -> PrimitiveRef<'a> {
self.painter
.prim(Primitive::sampling(self, Box::new(sampling)))
}
}
pub trait Structure {

View File

@ -1,6 +1,7 @@
mod gen;
pub mod plot;
mod tile;
pub mod util;
use self::tile::{HazardKind, KeepKind, Ori, RoofKind, Tile, TileGrid, TileKind, TILE_SIZE};
pub use self::{

View File

@ -1,5 +1,5 @@
use super::*;
use crate::Land;
use crate::{site2::util::Dir, Land};
use common::terrain::{Block, BlockKind, SpriteKind};
use rand::prelude::*;
use vek::*;
@ -118,7 +118,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height),
},
inset: roof_height,
dir: true,
dir: Dir::Y,
}),
painter.prim(Primitive::Gable {
aabb: Aabb {
@ -135,7 +135,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height - 1),
},
inset: roof_height - 1,
dir: true,
dir: Dir::Y,
}),
)
},
@ -154,7 +154,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height),
},
inset: roof_height,
dir: false,
dir: Dir::X,
}),
painter.prim(Primitive::Gable {
aabb: Aabb {
@ -171,7 +171,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height - 1),
},
inset: roof_height - 1,
dir: false,
dir: Dir::X,
}),
)
},
@ -190,7 +190,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height),
},
inset: roof_height,
dir: true,
dir: Dir::Y,
}),
painter.prim(Primitive::Gable {
aabb: Aabb {
@ -203,7 +203,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height - 1),
},
inset: roof_height - 1,
dir: true,
dir: Dir::Y,
}),
),
_ => (
@ -221,7 +221,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height),
},
inset: roof_height,
dir: false,
dir: Dir::X,
}),
painter.prim(Primitive::Gable {
aabb: Aabb {
@ -237,7 +237,7 @@ impl Structure for House {
.with_z(alt + roof + roof_height - 1),
},
inset: roof_height - 1,
dir: false,
dir: Dir::X,
}),
),
};
@ -905,7 +905,7 @@ impl Structure for House {
.with_z(alt + height),
},
inset: storey,
dir: 3,
dir: Dir::NegY,
}),
1 => painter.prim(Primitive::Ramp {
aabb: Aabb {
@ -921,7 +921,7 @@ impl Structure for House {
.with_z(alt + height),
},
inset: storey,
dir: 1,
dir: Dir::NegX,
}),
2 => painter.prim(Primitive::Ramp {
aabb: Aabb {
@ -937,7 +937,7 @@ impl Structure for House {
.with_z(alt + height),
},
inset: storey,
dir: 2,
dir: Dir::Y,
}),
_ => painter.prim(Primitive::Ramp {
aabb: Aabb {
@ -953,7 +953,7 @@ impl Structure for House {
.with_z(alt + height),
},
inset: storey,
dir: 0,
dir: Dir::X,
}),
};
let shed_empty = match self.front {
@ -971,7 +971,7 @@ impl Structure for House {
.with_z(alt + height - 1),
},
inset: storey - 1,
dir: 3,
dir: Dir::NegY,
}),
1 => painter.prim(Primitive::Ramp {
aabb: Aabb {
@ -987,7 +987,7 @@ impl Structure for House {
.with_z(alt + height - 1),
},
inset: storey - 1,
dir: 1,
dir: Dir::NegX,
}),
2 => painter.prim(Primitive::Ramp {
aabb: Aabb {
@ -1003,7 +1003,7 @@ impl Structure for House {
.with_z(alt + height),
},
inset: storey - 1,
dir: 2,
dir: Dir::Y,
}),
_ => painter.prim(Primitive::Ramp {
aabb: Aabb {
@ -1019,7 +1019,7 @@ impl Structure for House {
.with_z(alt + height),
},
inset: storey - 1,
dir: 0,
dir: Dir::X,
}),
};
painter.fill(
@ -1232,7 +1232,7 @@ impl Structure for House {
.with_z(alt + height + 1),
},
inset: 3,
dir: true,
dir: Dir::Y,
}),
1 => painter.prim(Primitive::Gable {
aabb: Aabb {
@ -1245,7 +1245,7 @@ impl Structure for House {
.with_z(alt + height + 1),
},
inset: 3,
dir: false,
dir: Dir::X,
}),
2 => painter.prim(Primitive::Gable {
aabb: Aabb {
@ -1258,7 +1258,7 @@ impl Structure for House {
.with_z(alt + height + 1),
},
inset: 3,
dir: true,
dir: Dir::Y,
}),
_ => painter.prim(Primitive::Gable {
aabb: Aabb {
@ -1271,7 +1271,7 @@ impl Structure for House {
.with_z(alt + height + 1),
},
inset: 3,
dir: false,
dir: Dir::X,
}),
};
let window_min = match self.front {
@ -1494,7 +1494,7 @@ impl Structure for House {
max: Vec2::new(stair_origin.x + 10, stair_origin.y + stair_width).with_z(alt + previous_height + 1),
},
inset: storey,
dir: 0,
dir: Dir::X,
})
/*},
1 => {
@ -1573,7 +1573,7 @@ impl Structure for House {
max: Vec2::new(stair_origin.x + 8, stair_origin.y + 2 * stair_width).with_z(alt + previous_height + 1),
},
inset: storey,
dir: 1,
dir: Dir::NegX,
})
/*},
1 => {

View File

@ -0,0 +1,92 @@
use vek::*;
/// A wrapping mode, used to determine what to do when sampling outside of 0..=1
#[derive(Clone, Copy)]
pub enum WrapMode {
/// ..............______
/// No repeat ___/
Clamp,
/// Saw wave repeat / / / /
Repeat,
/// Triangle wave repeat /\/\/\/\/
PingPong,
}
impl WrapMode {
fn sample(&self, t: f32) -> f32 {
match self {
WrapMode::Clamp => t.clamp(0.0, 1.0),
WrapMode::Repeat => (1.0 + t.fract()).fract(),
WrapMode::PingPong => 1.0 - 2.0 * ((t / 2.0).fract().abs() - 0.5).abs(),
}
}
}
#[derive(Clone, Copy)]
pub enum Shape {
Point,
/// Vector should be normalized for Gradient size to work properly
Plane(Vec3<f32>),
/// Vector should be normalized for Gradient size to work properly
Line(Vec3<f32>),
}
impl Shape {
/// Create a new plane shape with the given normal.
pub fn plane(normal: Vec3<f32>) -> Self { Shape::Plane(normal.normalized()) }
/// Create an infinite line shape with the given direction.
pub fn radial_line(direction: Vec3<f32>) -> Self { Shape::Line(direction.normalized()) }
}
#[derive(Clone)]
pub struct Gradient {
/// The center of the gradient shape
pub(super) center: Vec3<f32>,
/// The distance the gradient is sampled along
pub(super) size: f32,
/// The shape that the distance is computed to to get the gradient color.
pub(super) shape: Shape,
/// How the graduint should repeat when the distance from the shape is
/// greater than size
pub(super) repeat: WrapMode,
/// The colors the gradient is lerped between
pub(super) colors: (Rgb<u8>, Rgb<u8>),
}
impl Gradient {
pub fn new(center: Vec3<f32>, size: f32, shape: Shape, colors: (Rgb<u8>, Rgb<u8>)) -> Self {
Gradient {
center,
size,
shape,
repeat: WrapMode::Clamp,
colors,
}
}
/// Add a repeat mode to the gradient
#[must_use]
pub fn with_repeat(mut self, repeat: WrapMode) -> Self {
self.repeat = repeat;
self
}
/// Sample the gradient at a certain point, will always return a color
/// that's in the range color.0..=color.1
pub fn sample(&self, pos: Vec3<f32>) -> Rgb<u8> {
// Calculate t by dividing the distance from the shape divided by size
let t = self.repeat.sample(match self.shape {
Shape::Point => pos.distance(self.center) / self.size,
Shape::Plane(normal) => (pos - self.center).dot(normal) / self.size,
Shape::Line(line) => {
let u = pos - self.center;
(u.dot(line) * line - u).magnitude() / self.size
},
});
// Lerp colors
self.colors.0.map2(self.colors.1, |a, b| {
(a as f32 * (1.0 - t) + b as f32 * t) as u8
})
}
}

140
world/src/site2/util/mod.rs Normal file
View File

@ -0,0 +1,140 @@
pub mod gradient;
use rand::Rng;
use vek::*;
/// A 2d direction.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Dir {
X,
Y,
NegX,
NegY,
}
impl Dir {
pub fn choose(rng: &mut impl Rng) -> Dir {
match rng.gen_range(0..4) {
0 => Dir::X,
1 => Dir::Y,
3 => Dir::NegX,
_ => Dir::NegY,
}
}
pub fn from_vector(vec: Vec2<i32>) -> Dir {
if vec.x.abs() > vec.y.abs() {
if vec.x > 0 { Dir::X } else { Dir::NegX }
} else if vec.y > 0 {
Dir::Y
} else {
Dir::NegY
}
}
#[must_use]
pub fn opposite(self) -> Dir {
match self {
Dir::X => Dir::NegX,
Dir::NegX => Dir::X,
Dir::Y => Dir::NegY,
Dir::NegY => Dir::Y,
}
}
/// Rotate the direction anti clock wise
#[must_use]
pub fn rotate_left(self) -> Dir {
match self {
Dir::X => Dir::Y,
Dir::NegX => Dir::NegY,
Dir::Y => Dir::NegX,
Dir::NegY => Dir::X,
}
}
/// Rotate the direction clock wise
#[must_use]
pub fn rotate_right(self) -> Dir {
match self {
Dir::X => Dir::NegY,
Dir::NegX => Dir::Y,
Dir::Y => Dir::X,
Dir::NegY => Dir::NegX,
}
}
pub fn to_vec2(self) -> Vec2<i32> {
match self {
Dir::X => Vec2::new(1, 0),
Dir::NegX => Vec2::new(-1, 0),
Dir::Y => Vec2::new(0, 1),
Dir::NegY => Vec2::new(0, -1),
}
}
pub fn to_vec3(self) -> Vec3<i32> {
match self {
Dir::X => Vec3::new(1, 0, 0),
Dir::NegX => Vec3::new(-1, 0, 0),
Dir::Y => Vec3::new(0, 1, 0),
Dir::NegY => Vec3::new(0, -1, 0),
}
}
/// Returns a 3x3 matrix that rotates Vec3(1, 0, 0) to the direction you get
/// in to_vec3. Inteded to be used with Primitive::Rotate.
///
/// Example:
/// ```
/// use vek::Vec3;
/// use veloren_world::site2::util::Dir;
/// let dir = Dir::X;
///
/// assert_eq!(dir.to_mat3x3() * Vec3::new(1, 0, 0), dir.to_vec3());
///
/// let dir = Dir::NegX;
///
/// assert_eq!(dir.to_mat3x3() * Vec3::new(1, 0, 0), dir.to_vec3());
///
/// let dir = Dir::Y;
///
/// assert_eq!(dir.to_mat3x3() * Vec3::new(1, 0, 0), dir.to_vec3());
///
/// let dir = Dir::NegY;
///
/// assert_eq!(dir.to_mat3x3() * Vec3::new(1, 0, 0), dir.to_vec3());
/// ```
pub fn to_mat3x3(self) -> Mat3<i32> {
match self {
Dir::X => Mat3::new(1, 0, 0, 0, 1, 0, 0, 0, 1),
Dir::NegX => Mat3::new(-1, 0, 0, 0, -1, 0, 0, 0, 1),
Dir::Y => Mat3::new(0, -1, 0, 1, 0, 0, 0, 0, 1),
Dir::NegY => Mat3::new(0, 1, 0, -1, 0, 0, 0, 0, 1),
}
}
/// Translates this direction to worldspace as if it was relative to the
/// other direction
#[must_use]
pub fn relative_to(self, other: Dir) -> Dir {
match other {
Dir::X => self,
Dir::NegX => self.opposite(),
Dir::Y => self.rotate_right(),
Dir::NegY => self.rotate_left(),
}
}
/// Is this direction parallel to x
pub fn is_x(self) -> bool { matches!(self, Dir::X | Dir::NegX) }
/// Is this direction parallel to y
pub fn is_y(self) -> bool { matches!(self, Dir::Y | Dir::NegY) }
}
impl std::ops::Neg for Dir {
type Output = Dir;
fn neg(self) -> Self::Output { self.opposite() }
}