diff --git a/editor/src/plugins/tilemap/autotile.rs b/editor/src/plugins/tilemap/autotile.rs index 2716f18da..ba88b50fd 100644 --- a/editor/src/plugins/tilemap/autotile.rs +++ b/editor/src/plugins/tilemap/autotile.rs @@ -499,7 +499,12 @@ impl BrushMacro for AutoTileMacro { } } - fn begin_update(&mut self, context: &BrushMacroInstance, tile_map: &TileMapContext) { + fn begin_update( + &mut self, + context: &BrushMacroInstance, + stamp: &Stamp, + tile_map: &TileMapContext, + ) { let Some(tile_set) = tile_map.tile_set() else { self.context.clear(); return; @@ -515,6 +520,13 @@ impl BrushMacro for AutoTileMacro { return; }; let frequency_property = instance.frequency_property; + // Check that at least one cell in the stamp is part of this instance. + if !stamp.values().any(|StampElement { brush_cell, .. }| { + brush_cell.is_some_and(|handle| instance.cells.contains_key(&handle)) + }) { + self.context.clear(); + return; + } Log::verify(self.context.fill_pattern_map( &tile_set.data_ref(), pattern_property, @@ -528,6 +540,9 @@ impl BrushMacro for AutoTileMacro { update: &mut MacroTilesUpdate, tile_map: &TileMap, ) { + if self.context.is_empty() { + return; + } let Some(instance) = &context.settings() else { return; }; diff --git a/editor/src/plugins/tilemap/brush_macro.rs b/editor/src/plugins/tilemap/brush_macro.rs index 065c0dd3a..968ffa398 100644 --- a/editor/src/plugins/tilemap/brush_macro.rs +++ b/editor/src/plugins/tilemap/brush_macro.rs @@ -343,7 +343,12 @@ pub trait BrushMacro: 'static + Send + Sync { /// stored in the current brush when the user presses the mouse button down to begin a stroke /// and gives the macros a chance to prepare themselves for the series of [`amend_update`](BrushMacro::amend_update) /// calls that will follow. - fn begin_update(&mut self, context: &BrushMacroInstance, tile_map: &TileMapContext); + fn begin_update( + &mut self, + context: &BrushMacroInstance, + stamp: &Stamp, + tile_map: &TileMapContext, + ); /// This is called for every macro instance whenever the user uses a brush to update a tile map. /// - `context`: The macro instance. /// - `update`: The change that the user is attempting to make to the tile map. Modify this to diff --git a/editor/src/plugins/tilemap/interaction_mode.rs b/editor/src/plugins/tilemap/interaction_mode.rs index d2d222aeb..0c9e0497d 100644 --- a/editor/src/plugins/tilemap/interaction_mode.rs +++ b/editor/src/plugins/tilemap/interaction_mode.rs @@ -287,10 +287,13 @@ fn macro_begin( tile_map_context: &TileMapContext, update: &mut TransTilesUpdate, macro_update: &mut MacroTilesUpdate, - brush: &TileMapBrushResource, + stamp: &Stamp, macros: &mut BrushMacroList, macro_instances: &mut Vec<(Uuid, Option)>, ) { + let Some(brush) = stamp.brush() else { + return; + }; let tile_map_handle = tile_map_context.node; let Some(tile_map) = tile_map_context.engine.scenes[tile_map_context.scene].graph [tile_map_handle] @@ -324,7 +327,7 @@ fn macro_begin( continue; }; context.settings = settings; - brush_macro.begin_update(&context, tile_map_context); + brush_macro.begin_update(&context, stamp, tile_map_context); brush_macro.amend_update(&context, macro_update, tile_map); } macro_update.fill_trans_tiles_update(update); @@ -545,7 +548,7 @@ impl InteractionMode for TileMapInteractionMode { let update = &mut self.update_effect.lock().update; draw(update, tiles, mode, &state, grid_coord, grid_coord); drop(tiles_guard); - if let Some(brush) = state.stamp.brush() { + if state.stamp.brush().is_some() { let tile_map = TileMapContext { node: self.tile_map, scene: scene_handle, @@ -555,7 +558,7 @@ impl InteractionMode for TileMapInteractionMode { &tile_map, update, &mut self.macro_update, - brush, + &state.stamp, &mut self.brush_macro_list.lock(), &mut self.macro_instance_list, ); diff --git a/editor/src/plugins/tilemap/wfc.rs b/editor/src/plugins/tilemap/wfc.rs index e477233ae..6be2a01e8 100644 --- a/editor/src/plugins/tilemap/wfc.rs +++ b/editor/src/plugins/tilemap/wfc.rs @@ -617,7 +617,13 @@ impl BrushMacro for WfcMacro { fn sync_cell_editors(&mut self, _context: &MacroMessageContext, _ui: &mut UserInterface) {} - fn begin_update(&mut self, _context: &BrushMacroInstance, _tile_map: &TileMapContext) {} + fn begin_update( + &mut self, + _context: &BrushMacroInstance, + _stamp: &Stamp, + _tile_map: &TileMapContext, + ) { + } fn amend_update( &mut self, diff --git a/fyrox-autotile/src/auto.rs b/fyrox-autotile/src/auto.rs index 5ba79317b..31b8e0023 100644 --- a/fyrox-autotile/src/auto.rs +++ b/fyrox-autotile/src/auto.rs @@ -475,6 +475,10 @@ impl Default for AutoTileContext { } impl AutoTileContext { + /// True if this context contains no patterns. + pub fn is_empty(&self) -> bool { + self.patterns.is_empty() + } /// Make the context empty in preparation for building a new context. pub fn clear(&mut self) { for (_, mut list) in self.patterns.drain() { diff --git a/fyrox-autotile/src/wave.rs b/fyrox-autotile/src/wave.rs index a0c8ee993..d68357fbf 100644 --- a/fyrox-autotile/src/wave.rs +++ b/fyrox-autotile/src/wave.rs @@ -105,6 +105,7 @@ pub trait WfcConstrain { /// The type of patterns that wave function collapse will choose. type Pattern: Eq + Hash + Clone; /// Iterator for all the patterns that wave function collapse is allowed to choose. + /// Note that any pattern with a probability of 0.0 must not be included. fn all_patterns(&self) -> impl Iterator; /// The probability of choosing a given pattern when there are no restrictions on patterns /// in any of the adjacent cells. This should be between 0.0 and 1.0. @@ -567,9 +568,24 @@ impl WfcPropagator, Pos: Debug, { - for p in constraint.all_patterns() { - if pattern != p { - self.after_restrict(position, p, constraint)?; + for offset in Pos::all_offsets() { + let other_pos = position.clone() + offset.clone(); + let Some(other_cell) = self.wave.get_mut(&other_pos) else { + continue; + }; + other_cell + .pattern_possibilities + .retain(|other_pattern, _counter| { + if !constraint.is_legal(pattern, &offset, other_pattern) { + self.pending + .push((other_pos.clone(), other_pattern.clone())); + false + } else { + true + } + }); + if other_cell.pattern_possibilities.is_empty() { + return Err(WfcFailure); } } Ok(())