Skip to content

Commit

Permalink
feat: add brush ID randomization setting
Browse files Browse the repository at this point in the history
  • Loading branch information
white-axe committed Apr 24, 2024
1 parent 941d125 commit d4a5485
Show file tree
Hide file tree
Showing 11 changed files with 126 additions and 32 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ rfd = "0.12.0"
tempfile = "3.8.1"

rand = "0.8.5"
murmur3 = "0.5.2"

alacritty_terminal = "0.22.0"

Expand Down
1 change: 1 addition & 0 deletions crates/components/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ fragile.workspace = true
parking_lot.workspace = true

fuzzy-matcher = "0.3.7"
murmur3.workspace = true
3 changes: 2 additions & 1 deletion crates/components/src/map_view.rs
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,8 @@ impl MapView {
);
let pattern_rect = egui::Rect::from_min_size(
map_rect.min + (self.cursor_pos.to_vec2() * tile_size),
if !force_show_pattern_rect && drawing_shape_pos.is_some() {
if tilepicker.brush_random || (!force_show_pattern_rect && drawing_shape_pos.is_some())
{
egui::Vec2::splat(tile_size)
} else {
egui::vec2(
Expand Down
55 changes: 52 additions & 3 deletions crates/components/src/tilepicker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ pub struct Tilepicker {
resources: Arc<Resources>,
viewport: Arc<luminol_graphics::viewport::Viewport>,
ani_time: Option<f64>,

/// When true, brush tile ID randomization is enabled.
pub brush_random: bool,
/// Seed for the PRNG used for the brush when brush tile ID randomization is enabled.
brush_seed: [u8; 16],
}

struct Resources {
Expand Down Expand Up @@ -174,6 +179,18 @@ impl Tilepicker {
&passages,
);

let mut brush_seed = [0u8; 16];
brush_seed[0..8].copy_from_slice(
&update_state
.project_config
.as_ref()
.expect("project not loaded")
.project
.persistence_id
.to_le_bytes(),
);
brush_seed[8..16].copy_from_slice(&(map_id as u64).to_le_bytes());

Ok(Self {
resources: Arc::new(Resources {
tiles,
Expand All @@ -189,14 +206,44 @@ impl Tilepicker {
coll_enabled: false,
grid_enabled: true,
drag_origin: None,
brush_seed,
brush_random: false,
})
}

pub fn get_tile_from_offset(&self, x: i16, y: i16) -> SelectedTile {
pub fn get_tile_from_offset(
&self,
absolute_x: i16,
absolute_y: i16,
absolute_z: i16,
relative_x: i16,
relative_y: i16,
) -> SelectedTile {
let width = self.selected_tiles_right - self.selected_tiles_left + 1;
let height = self.selected_tiles_bottom - self.selected_tiles_top + 1;
let x = self.selected_tiles_left + x.rem_euclid(width);
let y = self.selected_tiles_top + y.rem_euclid(height);

let (x, y) = if self.brush_random {
let mut preimage = [0u8; 40];
preimage[0..16].copy_from_slice(&self.brush_seed);
preimage[16..24].copy_from_slice(&(absolute_x as u64).to_le_bytes());
preimage[24..32].copy_from_slice(&(absolute_y as u64).to_le_bytes());
preimage[32..40].copy_from_slice(&(absolute_z as u64).to_le_bytes());
let image = murmur3::murmur3_32(&mut std::io::Cursor::new(preimage), 5381).unwrap();
let x = (image & 0xffff) as i16;
let y = (image >> 16) as i16;
(
self.selected_tiles_left
+ (self.selected_tiles_left + x.rem_euclid(width)).rem_euclid(width),
self.selected_tiles_top
+ (self.selected_tiles_top + y.rem_euclid(height)).rem_euclid(height),
)
} else {
(
self.selected_tiles_left + relative_x.rem_euclid(width),
self.selected_tiles_top + relative_y.rem_euclid(height),
)
};

match y {
..=0 => SelectedTile::Autotile(x),
_ => SelectedTile::Tile(x + (y - 1) * 8 + 384),
Expand All @@ -209,6 +256,8 @@ impl Tilepicker {
ui: &mut egui::Ui,
scroll_rect: egui::Rect,
) -> egui::Response {
self.brush_random = update_state.toolbar.brush_random != ui.input(|i| i.modifiers.alt);

let time = ui.ctx().input(|i| i.time);
let graphics_state = update_state.graphics.clone();

Expand Down
13 changes: 12 additions & 1 deletion crates/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,13 +147,14 @@ impl ModifiedState {
}

#[allow(missing_docs)]
#[derive(Default)]
pub struct ToolbarState {
/// The currently selected pencil.
pub pencil: Pencil,
/// Brush density between 0 and 1 inclusive; determines the proportion of randomly chosen tiles
/// the brush draws on if less than 1
pub brush_density: f32,
/// Whether or not brush tile ID randomization is active.
pub brush_random: bool,
}

#[derive(Default, strum::EnumIter, strum::Display, PartialEq, Eq, Clone, Copy)]
Expand All @@ -166,6 +167,16 @@ pub enum Pencil {
Fill,
}

impl Default for ToolbarState {
fn default() -> Self {
Self {
pencil: Default::default(),
brush_density: 1.,
brush_random: false,
}
}
}

impl<'res> UpdateState<'res> {
pub(crate) fn reborrow_with_edit_window<'this>(
&'this mut self,
Expand Down
2 changes: 1 addition & 1 deletion crates/ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ color-eyre.workspace = true

wgpu.workspace = true

murmur3 = "0.5.2"
murmur3.workspace = true

[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
luminol-term = { version = "0.4.0", path = "../term/" }
26 changes: 25 additions & 1 deletion crates/ui/src/tabs/map/brush.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,19 @@ impl super::Tab {

match pencil {
luminol_core::Pencil::Pen => {
let (rect_width, rect_height) = if self.tilepicker.brush_random {
(1, 1)
} else {
(width, height)
};

let drawing_shape_pos = if let Some(drawing_shape_pos) = self.drawing_shape_pos {
drawing_shape_pos
} else {
self.drawing_shape_pos = Some(map_pos);
map_pos
};
for (y, x) in (0..height).cartesian_product(0..width) {
for (y, x) in (0..rect_height).cartesian_product(0..rect_width) {
let absolute_x = map_x + x as usize;
let absolute_y = map_y + y as usize;

Expand All @@ -64,6 +70,9 @@ impl super::Tab {
self.set_tile(
map,
self.tilepicker.get_tile_from_offset(
absolute_x as i16,
absolute_y as i16,
tile_layer as i16,
x + (map_x as f32 - drawing_shape_pos.x) as i16,
y + (map_y as f32 - drawing_shape_pos.y) as i16,
),
Expand All @@ -87,6 +96,9 @@ impl super::Tab {
self.set_tile(
map,
self.tilepicker.get_tile_from_offset(
position.0 as i16,
position.1 as i16,
tile_layer as i16,
position.0 as i16 - drawing_shape_pos.x as i16,
position.1 as i16 - drawing_shape_pos.y as i16,
),
Expand Down Expand Up @@ -159,6 +171,9 @@ impl super::Tab {
self.set_tile(
map,
self.tilepicker.get_tile_from_offset(
x as i16,
y as i16,
tile_layer as i16,
x as i16 - drawing_shape_pos.x as i16,
y as i16 - drawing_shape_pos.y as i16,
),
Expand Down Expand Up @@ -202,6 +217,9 @@ impl super::Tab {
self.set_tile(
map,
self.tilepicker.get_tile_from_offset(
map_x as i16,
map_y as i16,
tile_layer as i16,
map_x as i16 - drawing_shape_pos.x as i16,
map_y as i16 - drawing_shape_pos.y as i16,
),
Expand Down Expand Up @@ -250,6 +268,9 @@ impl super::Tab {
self.set_tile(
map,
self.tilepicker.get_tile_from_offset(
x as i16,
y as i16,
tile_layer as i16,
x as i16 - drawing_shape_pos.x as i16,
y as i16 - drawing_shape_pos.y as i16,
),
Expand Down Expand Up @@ -292,6 +313,9 @@ impl super::Tab {
self.set_tile(
map,
self.tilepicker.get_tile_from_offset(
x as i16,
y as i16,
tile_layer as i16,
x as i16 - drawing_shape_pos.x as i16,
y as i16 - drawing_shape_pos.y as i16,
),
Expand Down
22 changes: 14 additions & 8 deletions crates/ui/src/tabs/map/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ pub struct Tab {
/// the brush draws on if less than 1
brush_density: f32,
/// Seed for the PRNG used for the brush when brush density is less than 1
brush_seed: [u8; 8],
brush_seed: [u8; 16],
}

// TODO: If we add support for changing event IDs, these need to be added as history entries
Expand Down Expand Up @@ -139,6 +139,18 @@ impl Tab {
|x, y, passage| passages[(x, y)] = passage,
);

let mut brush_seed = [0u8; 16];
brush_seed[0..8].copy_from_slice(
&update_state
.project_config
.as_ref()
.expect("project not loaded")
.project
.persistence_id
.to_le_bytes(),
);
brush_seed[8..16].copy_from_slice(&(id as u64).to_le_bytes());

Ok(Self {
id,

Expand All @@ -165,13 +177,7 @@ impl Tab {
passages,

brush_density: 1.,
brush_seed: update_state
.project_config
.as_ref()
.expect("project not loaded")
.project
.persistence_id
.to_le_bytes(),
brush_seed,
})
}
}
Expand Down
14 changes: 7 additions & 7 deletions crates/ui/src/tabs/map/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,17 +154,17 @@ impl super::Tab {
}

// Pick a pseudorandom normal f32 uniformly in the interval [0, 1)
let mut image = [0u8; 40];
image[0..8].copy_from_slice(&self.brush_seed);
image[8..16].copy_from_slice(&(position.0 as u64).to_le_bytes());
image[16..24].copy_from_slice(&(position.1 as u64).to_le_bytes());
image[24..32].copy_from_slice(&(position.2 as u64).to_le_bytes());
let f = (murmur3::murmur3_32(&mut std::io::Cursor::new(image), self.id as u32).unwrap()
let mut preimage = [0u8; 40];
preimage[0..16].copy_from_slice(&self.brush_seed);
preimage[16..24].copy_from_slice(&(position.0 as u64).to_le_bytes());
preimage[24..32].copy_from_slice(&(position.1 as u64).to_le_bytes());
preimage[32..40].copy_from_slice(&(position.2 as u64).to_le_bytes());
let image = (murmur3::murmur3_32(&mut std::io::Cursor::new(preimage), 1729).unwrap()
& 16777215) as f32
/ 16777216f32;

// Set the tile only if that's less than the brush density
if f >= self.brush_density {
if image >= self.brush_density {
return;
}
}
Expand Down
20 changes: 10 additions & 10 deletions src/app/top_bar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -424,19 +424,19 @@ impl TopBar {
ui.selectable_value(&mut update_state.toolbar.pencil, brush, brush.to_string());
}

ui.separator();

ui.vertical(|ui| {
ui.add_space(ui.spacing().button_padding.y.max(
(ui.spacing().interact_size.y - ui.text_style_height(&egui::TextStyle::Body)) / 2.,
));
ui.label("Brush Density:");
});

ui.add(egui::Slider::new(
&mut update_state.toolbar.brush_density,
0.0..=1.0,
));
))
.on_hover_text("The proportion of tiles the brush is able to draw on");

let alt_down = ui.input(|i| i.modifiers.alt);
let mut brush_random = update_state.toolbar.brush_random != alt_down;
ui.add(egui::Checkbox::new(
&mut brush_random, "Randomize ID",
))
.on_hover_text("If enabled, the brush will randomly place tiles out of the selected tiles in the tilepicker instead of placing them in a pattern");
update_state.toolbar.brush_random = brush_random != alt_down;

if open_project {
update_state.project_manager.open_project_picker();
Expand Down

0 comments on commit d4a5485

Please sign in to comment.