From 8e3445c742643202af959dbe450ceb38af27f358 Mon Sep 17 00:00:00 2001 From: Hamir Mahal Date: Sun, 13 Oct 2024 13:48:48 -0700 Subject: [PATCH] style: simplify string formatting for readability (#686) * style: simplify string formatting for readability * fix: formatting in 15 files --- editor/src/absm/parameter.rs | 2 +- editor/src/absm/state_viewer/mod.rs | 2 +- editor/src/animation/ruler.rs | 2 +- editor/src/animation/toolbar.rs | 2 +- editor/src/animation/track.rs | 14 +- editor/src/asset/item.rs | 2 +- editor/src/asset/mod.rs | 10 +- editor/src/command/mod.rs | 17 +- editor/src/export.rs | 46 +- editor/src/inspector/editors/material.rs | 2 +- editor/src/inspector/mod.rs | 2 +- editor/src/interaction/terrain.rs | 2 +- editor/src/lib.rs | 37 +- editor/src/light.rs | 7 +- editor/src/material.rs | 2 +- editor/src/plugins/tilemap/tileset.rs | 2 +- editor/src/scene/mod.rs | 15 +- editor/src/scene/property.rs | 10 +- editor/src/settings/build.rs | 2 +- editor/src/settings/mod.rs | 3 +- editor/src/utils/ragdoll.rs | 2 +- editor/src/world/mod.rs | 2 +- fyrox-animation/src/value.rs | 8 +- fyrox-core-derive/src/reflect/prop.rs | 4 +- fyrox-core-derive/src/visit.rs | 2 +- fyrox-core-derive/src/visit/utils.rs | 7 +- fyrox-core-derive/tests/it/visit.rs | 4 +- fyrox-core/src/log.rs | 4 +- fyrox-core/src/net.rs | 6 +- fyrox-core/src/pool/mod.rs | 28 +- fyrox-core/src/reflect.rs | 4 +- fyrox-core/src/variable.rs | 4 +- fyrox-core/src/visitor.rs | 72 +- fyrox-graph/src/lib.rs | 14 +- fyrox-graphics/src/error.rs | 4 +- fyrox-graphics/src/gl/program.rs | 31 +- fyrox-impl/src/engine/error.rs | 2 +- fyrox-impl/src/engine/executor.rs | 4 +- fyrox-impl/src/engine/mod.rs | 11 +- fyrox-impl/src/material/mod.rs | 12 +- fyrox-impl/src/renderer/cache/shader.rs | 5 +- fyrox-impl/src/renderer/cache/texture.rs | 5 +- fyrox-impl/src/renderer/mod.rs | 5 +- .../src/resource/fbx/document/attribute.rs | 22 +- fyrox-impl/src/resource/fbx/document/mod.rs | 7 +- fyrox-impl/src/resource/fbx/mod.rs | 13 +- fyrox-impl/src/resource/fbx/scene/light.rs | 5 +- fyrox-impl/src/resource/model/mod.rs | 6 +- fyrox-impl/src/resource/texture/mod.rs | 5 +- fyrox-impl/src/scene/base.rs | 3 +- fyrox-impl/src/scene/graph/mod.rs | 6 +- fyrox-impl/src/scene/mesh/surface.rs | 3 +- fyrox-impl/src/scene/mod.rs | 6 +- fyrox-impl/src/scene/node/container.rs | 8 +- .../src/scene/particle_system/emitter/mod.rs | 2 +- fyrox-impl/src/scene/sound/context.rs | 5 +- fyrox-impl/src/scene/sound/mod.rs | 2 +- .../src/scene/terrain/brushstroke/mod.rs | 2094 ++++++++--------- fyrox-impl/src/scene/terrain/geometry.rs | 6 +- fyrox-impl/src/scene/terrain/mod.rs | 2 +- fyrox-impl/src/utils/astar.rs | 2 +- fyrox-impl/src/utils/lightmap.rs | 2 +- fyrox-sound/src/error.rs | 8 +- fyrox-ui/src/curve/mod.rs | 4 +- fyrox-ui/src/inspector/mod.rs | 9 +- fyrox-ui/src/lib.rs | 7 +- fyrox-ui/src/node/container.rs | 2 +- project-manager/src/main.rs | 6 +- template-core/src/lib.rs | 22 +- template/src/main.rs | 9 +- 70 files changed, 1288 insertions(+), 1399 deletions(-) diff --git a/editor/src/absm/parameter.rs b/editor/src/absm/parameter.rs index c18a74677..e915be41f 100644 --- a/editor/src/absm/parameter.rs +++ b/editor/src/absm/parameter.rs @@ -128,7 +128,7 @@ impl ParameterPanel { if let Err(sync_errors) = ctx.sync(parameters, ui, 0, true, Default::default()) { for error in sync_errors { - Log::err(format!("Failed to sync property. Reason: {:?}", error)) + Log::err(format!("Failed to sync property. Reason: {error:?}")) } } } diff --git a/editor/src/absm/state_viewer/mod.rs b/editor/src/absm/state_viewer/mod.rs index 5538dab0a..a2a95be01 100644 --- a/editor/src/absm/state_viewer/mod.rs +++ b/editor/src/absm/state_viewer/mod.rs @@ -238,7 +238,7 @@ impl StateViewer { ui.send_message(WindowMessage::title( self.window, MessageDirection::ToWidget, - WindowTitle::text(format!("State Viewer - {}", state_name)), + WindowTitle::text(format!("State Viewer - {state_name}")), )); ui.send_message(WidgetMessage::enabled( diff --git a/editor/src/animation/ruler.rs b/editor/src/animation/ruler.rs index 53fbca4a9..09d69c4c3 100644 --- a/editor/src/animation/ruler.rs +++ b/editor/src/animation/ruler.rs @@ -247,7 +247,7 @@ impl Control for Ruler { let mut text = self.text.borrow_mut(); for x in self.transform.x_step_iter(STANDARD_GRID_SIZE) { - text.set_text(format!("{:.1}s", x)).build(); + text.set_text(format!("{x:.1}s")).build(); let vx = self.local_to_view(x); ctx.draw_text(self.clip_bounds(), Vector2::new(vx + 1.0, 0.0), &text); } diff --git a/editor/src/animation/toolbar.rs b/editor/src/animation/toolbar.rs index a3456a1d9..4d38587ba 100644 --- a/editor/src/animation/toolbar.rs +++ b/editor/src/animation/toolbar.rs @@ -1087,7 +1087,7 @@ impl Toolbar { animation.set_name(if i == 0 { file_stem.clone() } else { - format!("{} {}", file_stem, i) + format!("{file_stem} {i}") }); } diff --git a/editor/src/animation/track.rs b/editor/src/animation/track.rs index 276db500f..9e3a2277a 100644 --- a/editor/src/animation/track.rs +++ b/editor/src/animation/track.rs @@ -953,8 +953,7 @@ impl TrackList { } Err(e) => { Log::err(format!( - "Invalid property path {:?}. Error: {:?}!", - property_path, e + "Invalid property path {property_path:?}. Error: {e:?}!" )); } }) @@ -1227,7 +1226,7 @@ impl TrackList { } } Err(e) => { - Log::err(format!("Invalid property path {:?}. Error: {:?}!", desc, e)); + Log::err(format!("Invalid property path {desc:?}. Error: {e:?}!")); } }) } @@ -1565,21 +1564,18 @@ impl TrackList { if let Some((_, type_)) = type_id_to_supported_type(property_type) { if *value_type != type_ { validation_result = Err(format!( - "Property type mismatch. Expected {:?}, got {:?}", - value_type, type_ + "Property type mismatch. Expected {value_type:?}, got {type_:?}" )); } } else { validation_result = Err(format!( - "Unsupported property type of {:?} type id.", - property_type + "Unsupported property type of {property_type:?} type id." )); } } Err(err) => { validation_result = Err(format!( - "Unable to resolve property path {}. Reason: {:?}", - name, err + "Unable to resolve property path {name}. Reason: {err:?}" )); } }); diff --git a/editor/src/asset/item.rs b/editor/src/asset/item.rs index e65c9ff51..e8c528668 100644 --- a/editor/src/asset/item.rs +++ b/editor/src/asset/item.rs @@ -314,7 +314,7 @@ impl AssetItemBuilder { .with_margin(Thickness::uniform(1.0)) .with_allow_drag(true) .with_foreground(Brush::Solid(Color::opaque(50, 50, 50))) - .with_tooltip(make_tooltip(ctx, &format!("{:?}", path))) + .with_tooltip(make_tooltip(ctx, &format!("{path:?}"))) .with_child( GridBuilder::new( WidgetBuilder::new() diff --git a/editor/src/asset/mod.rs b/editor/src/asset/mod.rs index 9df8e1d0b..e948214e3 100644 --- a/editor/src/asset/mod.rs +++ b/editor/src/asset/mod.rs @@ -109,8 +109,7 @@ fn execute_command(command: &mut Command) { match command.spawn() { Ok(mut process) => Log::verify(process.wait()), Err(err) => Log::err(format!( - "Failed to show asset item in explorer. Reason: {:?}", - err + "Failed to show asset item in explorer. Reason: {err:?}" )), } } @@ -508,7 +507,7 @@ impl ResourceCreator { asset_added = true; } - Err(e) => Log::err(format!("Unable to create a resource. Reason: {:?}", e)), + Err(e) => Log::err(format!("Unable to create a resource. Reason: {e:?}")), } } } @@ -1266,9 +1265,8 @@ impl AssetBrowser { }) { if ext == "fbx" { Log::warn(format!( - "Resource {} cannot be scanned for \ - references, because FBX cannot be exported.", - kind + "Resource {kind} cannot be scanned for \ + references, because FBX cannot be exported." )); return false; } diff --git a/editor/src/command/mod.rs b/editor/src/command/mod.rs index 42d9c258f..646bda0f7 100644 --- a/editor/src/command/mod.rs +++ b/editor/src/command/mod.rs @@ -210,7 +210,7 @@ impl CommandStack { ) { for mut dropped_command in commands.drain(range) { if debug { - println!("Finalizing command {:?}", dropped_command); + println!("Finalizing command {dropped_command:?}"); } dropped_command.finalize(context); } @@ -235,7 +235,7 @@ impl CommandStack { } if self.debug { - println!("Executing command {:?}", command); + println!("Executing command {command:?}"); } command.execute(context); @@ -248,7 +248,7 @@ impl CommandStack { if let Some(top) = self.top.as_mut() { if let Some(command) = self.commands.get_mut(*top) { if self.debug { - println!("Undo command {:?}", command); + println!("Undo command {command:?}"); } command.revert(context) } @@ -281,7 +281,7 @@ impl CommandStack { if let Some(command) = command { if self.debug { - println!("Redo command {:?}", command); + println!("Redo command {command:?}"); } command.execute(context) } @@ -291,7 +291,7 @@ impl CommandStack { pub fn clear(&mut self, context: &mut dyn CommandContext) { for mut dropped_command in self.commands.drain(..) { if self.debug { - println!("Finalizing command {:?}", dropped_command); + println!("Finalizing command {dropped_command:?}"); } dropped_command.finalize(context); } @@ -329,10 +329,9 @@ where let mut func = Some(func); entity.resolve_path_mut(path, &mut |result| match result { Ok(field) => func.take().unwrap()(field), - Err(e) => fyrox::core::log::Log::err(format!( - "There is no such property {}! Reason: {:?}", - path, e - )), + Err(e) => { + fyrox::core::log::Log::err(format!("There is no such property {path}! Reason: {e:?}")) + } }) } diff --git a/editor/src/export.rs b/editor/src/export.rs index af776892d..08c56dd10 100644 --- a/editor/src/export.rs +++ b/editor/src/export.rs @@ -193,19 +193,12 @@ fn read_metadata() -> Result { Ok(output) => match serde_json::from_slice::(&output.stdout) { Ok(metadata) => Ok(metadata), Err(err) => Err(format!( - "Unable to parse workspace metadata. Reason {:?}", - err + "Unable to parse workspace metadata. Reason {err:?}" )), }, - Err(err) => Err(format!( - "Unable to fetch project metadata. Reason {:?}", - err - )), + Err(err) => Err(format!("Unable to fetch project metadata. Reason {err:?}")), }, - Err(err) => Err(format!( - "Unable to fetch project metadata. Reason {:?}", - err - )), + Err(err) => Err(format!("Unable to fetch project metadata. Reason {err:?}")), }; } @@ -215,8 +208,7 @@ fn prepare_build_dir(path: &Path) -> Result<(), String> { if let Err(err) = fs::remove_dir_all(path) { return Err(format!( - "Unable to remove previous build at destination path! Reason: {:?}", - err + "Unable to remove previous build at destination path! Reason: {err:?}" )); } } @@ -224,8 +216,7 @@ fn prepare_build_dir(path: &Path) -> Result<(), String> { // Create the new clean folder. if let Err(err) = fs::create_dir_all(path) { return Err(format!( - "Unable to create build directory at destination path! Reason: {:?}", - err + "Unable to create build directory at destination path! Reason: {err:?}" )); } @@ -268,14 +259,14 @@ fn cargo_install(crate_name: &str) -> Result<(), String> { Err(String::from_utf8_lossy(&output.stderr).to_string()) } } - Err(err) => Err(format!("Unable to install {crate_name}. Reason: {:?}", err)), + Err(err) => Err(format!("Unable to install {crate_name}. Reason: {err:?}")), }, - Err(err) => Err(format!("Unable to install {crate_name}. Reason: {:?}", err)), + Err(err) => Err(format!("Unable to install {crate_name}. Reason: {err:?}")), } } fn install_build_target(target: &str) -> Result<(), String> { - Log::info(format!("Trying to install {} build target...", target)); + Log::info(format!("Trying to install {target} build target...")); let mut process = make_command("rustup"); match process @@ -288,7 +279,7 @@ fn install_build_target(target: &str) -> Result<(), String> { Ok(handle) => match handle.wait_with_output() { Ok(output) => { if output.status.code().unwrap_or(1) == 0 { - Log::info(format!("{} target installed successfully!", target)); + Log::info(format!("{target} target installed successfully!")); Ok(()) } else { @@ -296,13 +287,11 @@ fn install_build_target(target: &str) -> Result<(), String> { } } Err(err) => Err(format!( - "Unable to install {} target. Reason: {:?}", - target, err + "Unable to install {target} target. Reason: {err:?}" )), }, Err(err) => Err(format!( - "Unable to install {} target. Reason: {:?}", - target, err + "Unable to install {target} target. Reason: {err:?}" )), } } @@ -380,7 +369,7 @@ fn build_package( let mut handle = match process.spawn() { Ok(handle) => handle, Err(err) => { - return Err(format!("Failed to build the game. Reason: {:?}", err)); + return Err(format!("Failed to build the game. Reason: {err:?}")); } }; @@ -411,7 +400,7 @@ fn build_package( } } Err(err) => { - return Err(format!("Failed to build the game. Reason: {:?}", err)); + return Err(format!("Failed to build the game. Reason: {err:?}")); } } @@ -547,8 +536,7 @@ fn export(export_options: ExportOptions, cancel_flag: Arc) -> Result let Some(package) = metadata.packages.iter().find(|p| p.name == package_name) else { return Err(format!( - "The project does not have `{}` package.", - package_name + "The project does not have `{package_name}` package." )); }; @@ -713,7 +701,7 @@ fn export(export_options: ExportOptions, cancel_flag: Arc) -> Result .spawn(), ); } - Err(err) => Log::err(format!("ADB error: {:?}", err)), + Err(err) => Log::err(format!("ADB error: {err:?}")), } } } @@ -1113,7 +1101,7 @@ impl ExportWindow { ctx.sync(&self.export_options, ui, 0, true, Default::default()) { for error in sync_errors { - Log::err(format!("Failed to sync property. Reason: {:?}", error)) + Log::err(format!("Failed to sync property. Reason: {error:?}")) } } } @@ -1156,7 +1144,7 @@ impl ExportWindow { Ok(_) => { Log::info("Build finished!"); } - Err(err) => Log::err(format!("Build failed! Reason: {}", err)), + Err(err) => Log::err(format!("Build failed! Reason: {err}")), } ui.send_message(WidgetMessage::enabled( diff --git a/editor/src/inspector/editors/material.rs b/editor/src/inspector/editors/material.rs index 319e27c7b..1e8d74e63 100644 --- a/editor/src/inspector/editors/material.rs +++ b/editor/src/inspector/editors/material.rs @@ -167,7 +167,7 @@ fn make_name(material: &MaterialResource) -> String { format!("{} - {} uses", header.kind, material.use_count()) } ResourceState::LoadError { ref error, .. } => { - format!("Loading failed: {:?}", error) + format!("Loading failed: {error:?}") } ResourceState::Pending { .. } => { format!("Loading {}", header.kind) diff --git a/editor/src/inspector/mod.rs b/editor/src/inspector/mod.rs index ca715cc0b..c8697ac33 100644 --- a/editor/src/inspector/mod.rs +++ b/editor/src/inspector/mod.rs @@ -146,7 +146,7 @@ fn print_errors(sync_errors: &[InspectorError]) { for error in sync_errors { Log::writeln( MessageKind::Error, - format!("Failed to sync property. Reason: {:?}", error), + format!("Failed to sync property. Reason: {error:?}"), ) } } diff --git a/editor/src/interaction/terrain.rs b/editor/src/interaction/terrain.rs index 193c77150..6dbd3d65e 100644 --- a/editor/src/interaction/terrain.rs +++ b/editor/src/interaction/terrain.rs @@ -680,7 +680,7 @@ impl BrushPanel { if let Err(e) = ctx.sync(brush, ui, 0, true, Default::default()) { Log::writeln( MessageKind::Error, - format!("Failed to sync BrushPanel's inspector. Reason: {:?}", e), + format!("Failed to sync BrushPanel's inspector. Reason: {e:?}"), ) } } diff --git a/editor/src/lib.rs b/editor/src/lib.rs index 43fc406c6..8dfb4b6a8 100644 --- a/editor/src/lib.rs +++ b/editor/src/lib.rs @@ -556,8 +556,7 @@ impl Editor { Log::info("Editor settings were loaded successfully!"); } Err(e) => Log::warn(format!( - "Failed to load settings, fallback to default. Reason: {:?}", - e + "Failed to load settings, fallback to default. Reason: {e:?}" )), } @@ -925,8 +924,7 @@ impl Editor { self.settings = Default::default(); Log::warn(format!( - "Failed to load settings, fallback to default. Reason: {:?}", - e + "Failed to load settings, fallback to default. Reason: {e:?}" )) } } @@ -945,10 +943,7 @@ impl Editor { Ok(_) => { Log::info("Graphics settings were applied successfully!"); } - Err(e) => Log::info(format!( - "Failed to apply graphics settings! Reason: {:?}", - e - )), + Err(e) => Log::info(format!("Failed to apply graphics settings! Reason: {e:?}")), } } @@ -1517,7 +1512,7 @@ impl Editor { self.on_mode_changed(); } - Err(e) => Log::err(format!("Failed to enter play mode: {:?}", e)), + Err(e) => Log::err(format!("Failed to enter play mode: {e:?}")), } } @@ -2116,7 +2111,7 @@ impl Editor { engine.resource_manager.state().set_watcher(Some(watcher)); } Err(e) => { - Log::err(format!("Unable to create resource watcher. Reason {:?}", e)); + Log::err(format!("Unable to create resource watcher. Reason {e:?}")); } } @@ -2129,8 +2124,7 @@ impl Editor { .on_configure(engine.user_interfaces.first(), &self.settings); Log::info(format!( - "New working directory was successfully set: {:?}", - working_directory + "New working directory was successfully set: {working_directory:?}" )); } @@ -2182,10 +2176,10 @@ impl Editor { self.mode = Mode::Edit; self.on_mode_changed(); - Log::warn(format!("Game was closed: {:?}", status)) + Log::warn(format!("Game was closed: {status:?}")) } } - Err(err) => Log::err(format!("Failed to wait for game process: {:?}", err)), + Err(err) => Log::err(format!("Failed to wait for game process: {err:?}")), } } Mode::Build { @@ -2194,7 +2188,7 @@ impl Editor { } => { if process.is_none() { if let Some(command) = queue.pop_front() { - Log::info(format!("Trying to run build command: {}", command)); + Log::info(format!("Trying to run build command: {command}")); let mut new_process = std::process::Command::new(&command.command); new_process @@ -2216,7 +2210,7 @@ impl Editor { *process = Some(new_process); } - Err(e) => Log::err(format!("Failed to enter build mode: {:?}", e)), + Err(e) => Log::err(format!("Failed to enter build mode: {e:?}")), } } else { Log::warn("Empty build command queue!"); @@ -2251,7 +2245,7 @@ impl Editor { } } } - Err(err) => Log::err(format!("Failed to wait for game process: {:?}", err)), + Err(err) => Log::err(format!("Failed to wait for game process: {err:?}")), } } } @@ -2728,10 +2722,7 @@ impl Editor { Ok(_) => { Log::info("Graphics settings were applied successfully!"); } - Err(e) => Log::err(format!( - "Failed to apply graphics settings! Reason: {:?}", - e - )), + Err(e) => Log::err(format!("Failed to apply graphics settings! Reason: {e:?}")), } } @@ -2788,7 +2779,7 @@ impl Editor { if let Err(e) = self.engine.set_frame_size((*size).into()) { fyrox::core::log::Log::writeln( MessageKind::Error, - format!("Failed to set renderer size! Reason: {:?}", e), + format!("Failed to set renderer size! Reason: {e:?}"), ); } @@ -2974,7 +2965,7 @@ fn update(editor: &mut Editor, window_target: &EventLoopWindowTarget<()>) { ); entry.selection = Default::default(); - Log::warn(format!("Command stack flushed for scene {}", i)); + Log::warn(format!("Command stack flushed for scene {i}")); } editor.message_sender.send(Message::SelectionChanged { diff --git a/editor/src/light.rs b/editor/src/light.rs index 4780ca4ba..60ce7053e 100644 --- a/editor/src/light.rs +++ b/editor/src/light.rs @@ -350,8 +350,7 @@ impl LightPanel { }) { Log::err(format!( - "Failed to create a new lightmap generation thread. Reason: {}", - e + "Failed to create a new lightmap generation thread. Reason: {e}" )) } } @@ -387,11 +386,11 @@ impl LightPanel { match result { Ok(lightmap) => { if let Err(err) = scene.graph.set_lightmap(lightmap) { - Log::err(format!("Failed to set generated lightmap. Reason: {}", err)); + Log::err(format!("Failed to set generated lightmap. Reason: {err}")); } } Err(err) => { - Log::err(format!("Failed to generated a lightmap. Reason: {}", err)); + Log::err(format!("Failed to generated a lightmap. Reason: {err}")); } } diff --git a/editor/src/material.rs b/editor/src/material.rs index f51899b43..55e77ee9b 100644 --- a/editor/src/material.rs +++ b/editor/src/material.rs @@ -493,7 +493,7 @@ impl MaterialEditor { let item = *self .properties .value_of(name) - .unwrap_or_else(|| panic!("Property not found {}", name)); + .unwrap_or_else(|| panic!("Property not found {name}")); match property_value { PropertyValue::Float(value) => { diff --git a/editor/src/plugins/tilemap/tileset.rs b/editor/src/plugins/tilemap/tileset.rs index 51377d06e..745ae7769 100644 --- a/editor/src/plugins/tilemap/tileset.rs +++ b/editor/src/plugins/tilemap/tileset.rs @@ -267,7 +267,7 @@ impl TileSetEditor { if let Err(sync_errors) = ctx.sync(tile_definition, ui, 0, true, Default::default()) { for error in sync_errors { - Log::err(format!("Failed to sync property. Reason: {:?}", error)) + Log::err(format!("Failed to sync property. Reason: {error:?}")) } } } diff --git a/editor/src/scene/mod.rs b/editor/src/scene/mod.rs index f62eaec64..ad45b323d 100644 --- a/editor/src/scene/mod.rs +++ b/editor/src/scene/mod.rs @@ -269,7 +269,7 @@ impl GameScene { pure_scene.save("Scene", &mut visitor).unwrap(); if let Err(e) = visitor.save_binary(path) { - Err(format!("Failed to save scene! Reason: {}", e)) + Err(format!("Failed to save scene! Reason: {e}")) } else { if settings.debugging.save_scene_in_text_form { let text = visitor.save_text(); @@ -447,20 +447,13 @@ impl GameScene { let mut visitor = Visitor::new(); match dest_scene.save("Scene", &mut visitor) { - Err(e) => Log::err(format!( - "Failed to save selection as prefab! Reason: {:?}", - e - )), + Err(e) => Log::err(format!("Failed to save selection as prefab! Reason: {e:?}")), Ok(_) => { if let Err(e) = visitor.save_binary(path) { - Log::err(format!( - "Failed to save selection as prefab! Reason: {:?}", - e - )); + Log::err(format!("Failed to save selection as prefab! Reason: {e:?}")); } else { Log::info(format!( - "Selection was successfully saved as prefab to {:?}!", - path + "Selection was successfully saved as prefab to {path:?}!" )) } } diff --git a/editor/src/scene/property.rs b/editor/src/scene/property.rs index 9b9358330..ca26d3336 100644 --- a/editor/src/scene/property.rs +++ b/editor/src/scene/property.rs @@ -198,10 +198,10 @@ where for i in 0..array.reflect_len() { let item = array.reflect_index(i).unwrap(); - let item_path = format!("{}[{}]", path, i); + let item_path = format!("{path}[{i}]"); descriptor.children_properties.push(PropertyDescriptor { path: item_path.clone(), - display_name: format!("[{}]", i), + display_name: format!("[{i}]"), type_name: item.type_name().to_owned(), type_id: item.type_id(), read_only: field_info.read_only, @@ -233,7 +233,7 @@ where // TODO: Here we just using `Debug` impl to obtain string representation for keys. This is // fine for most cases in the engine. - let mut key_str = format!("{:?}", key); + let mut key_str = format!("{key:?}"); let mut is_key_string = false; key.downcast_ref::(&mut |string| { @@ -251,11 +251,11 @@ where key_str.pop(); } - let item_path = format!("{}[{}]", path, key_str); + let item_path = format!("{path}[{key_str}]"); descriptor.children_properties.push(PropertyDescriptor { path: item_path.clone(), - display_name: format!("[{}]", key_str), + display_name: format!("[{key_str}]"), type_name: value.type_name().to_owned(), type_id: value.type_id(), read_only: field_info.read_only, diff --git a/editor/src/settings/build.rs b/editor/src/settings/build.rs index 7c5da5d53..0ea489ae4 100644 --- a/editor/src/settings/build.rs +++ b/editor/src/settings/build.rs @@ -47,7 +47,7 @@ impl Display for BuildCommand { write!(f, "{}", self.command)?; for arg in self.args.iter() { - write!(f, " {}", arg)?; + write!(f, " {arg}")?; } Ok(()) diff --git a/editor/src/settings/mod.rs b/editor/src/settings/mod.rs index c061e0fb8..48b018106 100644 --- a/editor/src/settings/mod.rs +++ b/editor/src/settings/mod.rs @@ -400,8 +400,7 @@ impl SettingsWindow { .set_quality_settings(&settings.graphics.quality) { Log::err(format!( - "An error occurred at attempt to set new graphics settings: {:?}", - e + "An error occurred at attempt to set new graphics settings: {e:?}" )); } else { Log::info("New graphics quality settings were successfully set!"); diff --git a/editor/src/utils/ragdoll.rs b/editor/src/utils/ragdoll.rs index d5e669b04..c3aa313b6 100644 --- a/editor/src/utils/ragdoll.rs +++ b/editor/src/utils/ragdoll.rs @@ -1212,7 +1212,7 @@ impl RagdollWizard { if let Err(sync_errors) = ctx.sync(&self.preset, ui, 0, true, Default::default()) { for error in sync_errors { - Log::err(format!("Failed to sync property. Reason: {:?}", error)) + Log::err(format!("Failed to sync property. Reason: {error:?}")) } } } diff --git a/editor/src/world/mod.rs b/editor/src/world/mod.rs index d8395aec2..2216f3a27 100644 --- a/editor/src/world/mod.rs +++ b/editor/src/world/mod.rs @@ -388,7 +388,7 @@ impl WorldViewer { .with_text(if self.breadcrumbs.is_empty() { name.to_owned() } else { - format!("{} >", name) + format!("{name} >") }) .with_font_size(11.0) .build(ctx), diff --git a/fyrox-animation/src/value.rs b/fyrox-animation/src/value.rs index a2c71235e..31ea30a03 100644 --- a/fyrox-animation/src/value.rs +++ b/fyrox-animation/src/value.rs @@ -305,7 +305,7 @@ impl Display for ValueBinding { ValueBinding::Position => write!(f, "Position"), ValueBinding::Scale => write!(f, "Scale"), ValueBinding::Rotation => write!(f, "Rotation"), - ValueBinding::Property { name, .. } => write!(f, "{}", name), + ValueBinding::Property { name, .. } => write!(f, "{name}"), } } } @@ -342,14 +342,12 @@ impl BoundValue { match err { SetFieldByPathError::InvalidPath { reason, .. } => { Log::err(format!( - "Failed to set property {}! Invalid path: {}", - property_name, reason + "Failed to set property {property_name}! Invalid path: {reason}" )); } SetFieldByPathError::InvalidValue(_) => { Log::err(format!( - "Failed to set property {}! Types mismatch!", - property_name + "Failed to set property {property_name}! Types mismatch!" )); } } diff --git a/fyrox-core-derive/src/reflect/prop.rs b/fyrox-core-derive/src/reflect/prop.rs index ce2e25985..c7ca76a3a 100644 --- a/fyrox-core-derive/src/reflect/prop.rs +++ b/fyrox-core-derive/src/reflect/prop.rs @@ -162,7 +162,7 @@ pub fn enum_prop_ident( let variant_ident = &variant_args.ident; let field_ident = self::field_ident_string(&variant_args.fields, nth, field); - let ident = format!("{}_{}", variant_ident, field_ident).to_case(Case::UpperSnake); + let ident = format!("{variant_ident}_{field_ident}").to_case(Case::UpperSnake); syn::parse_str(&ident).unwrap() } @@ -171,7 +171,7 @@ fn field_ident_string(fields: &args::Fields, nth: usize, field: &args::FieldArgs ast::Style::Struct => field.ident.as_ref().unwrap().to_string(), ast::Style::Tuple => { // this is actually `F_0` in UPPER_SNAKE_CASE - format!("F{}", nth) + format!("F{nth}") } ast::Style::Unit => unreachable!(), } diff --git a/fyrox-core-derive/src/visit.rs b/fyrox-core-derive/src/visit.rs index 6a88268b8..d0681f352 100644 --- a/fyrox-core-derive/src/visit.rs +++ b/fyrox-core-derive/src/visit.rs @@ -67,7 +67,7 @@ fn impl_visit_struct( /// impl `Visit` for `enum` fn impl_visit_enum(ty_args: &args::TypeArgs, variant_args: &[args::VariantArgs]) -> TokenStream2 { let ty_ident = &ty_args.ident; - let ty_name = format!("{}", ty_ident); + let ty_name = format!("{ty_ident}"); // variant ID = variant index let id_type = quote!(u32); diff --git a/fyrox-core-derive/src/visit/utils.rs b/fyrox-core-derive/src/visit/utils.rs index 5931fd55b..b961973d1 100644 --- a/fyrox-core-derive/src/visit/utils.rs +++ b/fyrox-core-derive/src/visit/utils.rs @@ -91,10 +91,7 @@ pub fn create_field_visits<'a>( ast::Style::Struct => { let ident = field.ident.as_ref().unwrap_or_else(|| unreachable!()); - ( - quote!(#ident), - format!("{}", ident).to_case(Case::UpperCamel), - ) + (quote!(#ident), format!("{ident}").to_case(Case::UpperCamel)) } // `Tuple(f32, ..)` ast::Style::Tuple => { @@ -108,7 +105,7 @@ pub fn create_field_visits<'a>( quote!(#ident) }; - (ident, format!("{}", field_index)) + (ident, format!("{field_index}")) } ast::Style::Unit => unreachable!(), }; diff --git a/fyrox-core-derive/tests/it/visit.rs b/fyrox-core-derive/tests/it/visit.rs index 7fceab9ee..377a9b401 100644 --- a/fyrox-core-derive/tests/it/visit.rs +++ b/fyrox-core-derive/tests/it/visit.rs @@ -38,8 +38,8 @@ pub fn save_load(test_name: &str, data: &mut T, data_default: &mut T) let root = PathBuf::from(manifest_dir).join("test_output"); let _ = std::fs::create_dir(&root); ( - root.join(format!("{}.bin", test_name)), - root.join(format!("{}.txt", test_name)), + root.join(format!("{test_name}.bin")), + root.join(format!("{test_name}.txt")), ) }; diff --git a/fyrox-core/src/log.rs b/fyrox-core/src/log.rs index b22e994b8..88f56f562 100644 --- a/fyrox-core/src/log.rs +++ b/fyrox-core/src/log.rs @@ -204,7 +204,7 @@ impl Log { if let Err(e) = result { Self::writeln( MessageKind::Error, - format!("Operation failed! Reason: {:?}", e), + format!("Operation failed! Reason: {e:?}"), ); } } @@ -221,7 +221,7 @@ impl Log { S: Display, { if let Err(e) = result { - Self::writeln(MessageKind::Error, format!("{}. Reason: {:?}", msg, e)); + Self::writeln(MessageKind::Error, format!("{msg}. Reason: {e:?}")); } } } diff --git a/fyrox-core/src/net.rs b/fyrox-core/src/net.rs index a9632b007..0ccc38f4f 100644 --- a/fyrox-core/src/net.rs +++ b/fyrox-core/src/net.rs @@ -121,8 +121,7 @@ impl NetStream { Ok(message) => Some(message), Err(err) => { Log::err(format!( - "Failed to parse a network message of {} bytes long. Reason: {:?}", - length, err + "Failed to parse a network message of {length} bytes long. Reason: {err:?}" )); None @@ -161,8 +160,7 @@ impl NetStream { } _ => { Log::err(format!( - "An error occurred when reading data from socket: {}", - err + "An error occurred when reading data from socket: {err}" )); self.rx_buffer.clear(); diff --git a/fyrox-core/src/pool/mod.rs b/fyrox-core/src/pool/mod.rs index 41ce74571..e3c15d5a6 100644 --- a/fyrox-core/src/pool/mod.rs +++ b/fyrox-core/src/pool/mod.rs @@ -471,8 +471,7 @@ where if record.payload.is_some() { panic!( - "Attempt to spawn an object at pool record with payload! Record index is {}", - free_index + "Attempt to spawn an object at pool record with payload! Record index is {free_index}" ); } @@ -527,8 +526,7 @@ where if record.payload.is_some() { panic!( - "Attempt to spawn an object at pool record with payload! Record index is {}", - free_index + "Attempt to spawn an object at pool record with payload! Record index is {free_index}" ); } @@ -605,7 +603,7 @@ where if let Some(payload) = record.payload.as_ref() { payload } else { - panic!("Attempt to borrow destroyed object at {:?} handle.", handle); + panic!("Attempt to borrow destroyed object at {handle:?} handle."); } } else { panic!( @@ -648,15 +646,14 @@ where if let Some(payload) = record.payload.as_mut() { payload } else { - panic!("Attempt to borrow destroyed object at {:?} handle.", handle); + panic!("Attempt to borrow destroyed object at {handle:?} handle."); } } else { panic!("Attempt to borrow object using dangling handle {:?}. Record has {} generation!", handle, record.generation); } } else { panic!( - "Attempt to borrow object using out-of-bounds handle {:?}! Record count is {}", - handle, record_count + "Attempt to borrow object using out-of-bounds handle {handle:?}! Record count is {record_count}" ); } } @@ -852,7 +849,7 @@ where if let Some(payload) = record.payload.take() { payload } else { - panic!("Attempt to double free object at handle {:?}!", handle); + panic!("Attempt to double free object at handle {handle:?}!"); } } else { panic!( @@ -916,10 +913,7 @@ where }; (ticket, payload) } else { - panic!( - "Attempt to take already taken object at handle {:?}!", - handle - ); + panic!("Attempt to take already taken object at handle {handle:?}!"); } } else { panic!( @@ -1521,16 +1515,16 @@ mod test { pool.free(d); let baz = pool.spawn("Baz".to_string()); for s in pool.iter() { - println!("{}", s); + println!("{s}"); } for s in pool.iter_mut() { - println!("{}", s); + println!("{s}"); } for s in &pool { - println!("{}", s); + println!("{s}"); } for s in &mut pool { - println!("{}", s); + println!("{s}"); } pool.free(foobar); pool.free(baz); diff --git a/fyrox-core/src/reflect.rs b/fyrox-core/src/reflect.rs index c330eb714..40aca7cec 100644 --- a/fyrox-core/src/reflect.rs +++ b/fyrox-core/src/reflect.rs @@ -889,7 +889,7 @@ impl dyn Reflect { if let Some((key, value)) = hash_map.reflect_get_at(i) { // TODO: Here we just using `Debug` impl to obtain string representation for keys. This is // fine for most cases in the engine. - let mut key_str = format!("{:?}", key); + let mut key_str = format!("{key:?}"); let mut is_key_string = false; key.downcast_ref::(&mut |string| is_key_string |= string.is_some()); @@ -905,7 +905,7 @@ impl dyn Reflect { key_str.pop(); } - let item_path = format!("{}[{}]", path, key_str); + let item_path = format!("{path}[{key_str}]"); value.enumerate_fields_recursively_internal( &item_path, diff --git a/fyrox-core/src/variable.rs b/fyrox-core/src/variable.rs index 0cd17b36e..69e1b9c94 100644 --- a/fyrox-core/src/variable.rs +++ b/fyrox-core/src/variable.rs @@ -1000,10 +1000,10 @@ mod test { #[test] fn inheritable_variable_try_sync_model() { let v = InheritableVariable::from(42); - assert!(!v.try_sync_model(|s| println!("{}", s))); + assert!(!v.try_sync_model(|s| println!("{s}"))); let v = InheritableVariable::new_with_flags(42, VariableFlags::NEED_SYNC); - assert!(v.try_sync_model(|s| println!("{}", s))); + assert!(v.try_sync_model(|s| println!("{s}"))); } #[test] diff --git a/fyrox-core/src/visitor.rs b/fyrox-core/src/visitor.rs index 983f79493..58e41c992 100644 --- a/fyrox-core/src/visitor.rs +++ b/fyrox-core/src/visitor.rs @@ -295,17 +295,17 @@ impl<'a, T: Pod> Visit for PodVecView<'a, T> { impl FieldKind { fn as_string(&self) -> String { match self { - Self::Bool(data) => format!(", ", data), - Self::U8(data) => format!(", ", data), - Self::I8(data) => format!(", ", data), - Self::U16(data) => format!(", ", data), - Self::I16(data) => format!(", ", data), - Self::U32(data) => format!(", ", data), - Self::I32(data) => format!(", ", data), - Self::U64(data) => format!(", ", data), - Self::I64(data) => format!(", ", data), - Self::F32(data) => format!(", ", data), - Self::F64(data) => format!(", ", data), + Self::Bool(data) => format!(", "), + Self::U8(data) => format!(", "), + Self::I8(data) => format!(", "), + Self::U16(data) => format!(", "), + Self::I16(data) => format!(", "), + Self::U32(data) => format!(", "), + Self::I32(data) => format!(", "), + Self::U64(data) => format!(", "), + Self::I64(data) => format!(", "), + Self::F32(data) => format!(", "), + Self::F64(data) => format!(", "), Self::Vector2F32(data) => format!(", ", data.x, data.y), Self::Vector3F32(data) => format!(", ", data.x, data.y, data.z), Self::Vector4F32(data) => { @@ -396,7 +396,7 @@ impl FieldKind { Self::Matrix4(data) => { let mut out = String::from(" s, Err(_) => base64::engine::general_purpose::STANDARD.encode(data), }; - format!(", ", out) + format!(", ") } Self::Matrix3(data) => { let mut out = String::from(" { - format!(" { format!(", ", data.re, data.im) @@ -426,15 +426,12 @@ impl FieldKind { bytes, } => { let base64_encoded = base64::engine::general_purpose::STANDARD.encode(bytes); - format!( - "", - type_id, element_size, base64_encoded - ) + format!("") } Self::Matrix2(data) => { let mut out = String::from(" std::fmt::Result { match self { - Self::Io(io) => write!(f, "io error: {}", io), - Self::UnknownFieldType(type_index) => write!(f, "unknown field type {}", type_index), - Self::FieldDoesNotExist(name) => write!(f, "field does not exists {}", name), - Self::FieldAlreadyExists(name) => write!(f, "field already exists {}", name), - Self::RegionAlreadyExists(name) => write!(f, "region already exists {}", name), + Self::Io(io) => write!(f, "io error: {io}"), + Self::UnknownFieldType(type_index) => write!(f, "unknown field type {type_index}"), + Self::FieldDoesNotExist(name) => write!(f, "field does not exists {name}"), + Self::FieldAlreadyExists(name) => write!(f, "field already exists {name}"), + Self::RegionAlreadyExists(name) => write!(f, "region already exists {name}"), Self::InvalidCurrentNode => write!(f, "invalid current node"), Self::FieldTypeDoesNotMatch => write!(f, "field type does not match"), - Self::RegionDoesNotExist(name) => write!(f, "region does not exists {}", name), + Self::RegionDoesNotExist(name) => write!(f, "region does not exists {name}"), Self::NoActiveNode => write!(f, "no active node"), Self::NotSupportedFormat => write!(f, "not supported format"), Self::InvalidName => write!(f, "invalid name"), Self::TypeMismatch => write!(f, "type mismatch"), Self::RefCellAlreadyMutableBorrowed => write!(f, "ref cell already mutable borrowed"), - Self::User(msg) => write!(f, "user defined error: {}", msg), + Self::User(msg) => write!(f, "user defined error: {msg}"), Self::UnexpectedRcNullIndex => write!(f, "unexpected rc null index"), Self::PoisonedMutex => write!(f, "attempt to lock poisoned mutex"), - Self::FileLoadError(e) => write!(f, "file load error: {:?}", e), + Self::FileLoadError(e) => write!(f, "file load error: {e:?}"), } } } @@ -1590,7 +1587,7 @@ where if region.reading { self.clear(); for index in 0..len { - let region_name = format!("Item{}", index); + let region_name = format!("Item{index}"); let mut region = region.enter_region(region_name.as_str())?; let mut object = T::default(); object.visit("ItemData", &mut region)?; @@ -1598,7 +1595,7 @@ where } } else { for (index, item) in self.iter_mut().enumerate() { - let region_name = format!("Item{}", index); + let region_name = format!("Item{index}"); let mut region = region.enter_region(region_name.as_str())?; item.visit("ItemData", &mut region)?; } @@ -1951,7 +1948,7 @@ where if region.is_reading() { self.clear(); for i in 0..(count as usize) { - let name = format!("Item{}", i); + let name = format!("Item{i}"); let mut region = region.enter_region(name.as_str())?; @@ -1965,7 +1962,7 @@ where } } else { for (i, (key, value)) in self.iter_mut().enumerate() { - let name = format!("Item{}", i); + let name = format!("Item{i}"); let mut region = region.enter_region(name.as_str())?; @@ -1994,7 +1991,7 @@ where if region.is_reading() { self.clear(); for i in 0..(count as usize) { - let name = format!("Item{}", i); + let name = format!("Item{i}"); let mut region = region.enter_region(name.as_str())?; @@ -2005,7 +2002,7 @@ where } } else { for (i, mut key) in self.clone().into_iter().enumerate() { - let name = format!("Item{}", i); + let name = format!("Item{i}"); let mut region = region.enter_region(name.as_str())?; @@ -2027,13 +2024,12 @@ impl Visit for [T; SIZE] { if region.reading { if len > SIZE as u32 { return VisitResult::Err(VisitError::User(format!( - "Not enough space in static array, got {}, needed {}!", - len, SIZE + "Not enough space in static array, got {len}, needed {SIZE}!" ))); } for index in 0..len { - let region_name = format!("Item{}", index); + let region_name = format!("Item{index}"); let mut region = region.enter_region(region_name.as_str())?; let mut object = T::default(); object.visit("ItemData", &mut region)?; @@ -2041,7 +2037,7 @@ impl Visit for [T; SIZE] { } } else { for (index, item) in self.iter_mut().enumerate() { - let region_name = format!("Item{}", index); + let region_name = format!("Item{index}"); let mut region = region.enter_region(region_name.as_str())?; item.visit("ItemData", &mut region)?; } diff --git a/fyrox-graph/src/lib.rs b/fyrox-graph/src/lib.rs index e151ae36f..b9d3a0f42 100644 --- a/fyrox-graph/src/lib.rs +++ b/fyrox-graph/src/lib.rs @@ -497,8 +497,7 @@ pub trait SceneGraphNode: AbstractSceneNode + Clone + 'static { } }), Err(e) => Log::err(format!( - "Failed to resolve parent path {}. Reason: {:?}", - path, e + "Failed to resolve parent path {path}. Reason: {e:?}" )), }) }); @@ -513,13 +512,12 @@ pub trait SceneGraphNode: AbstractSceneNode + Clone + 'static { if let Some(child_inheritable) = child_inheritable { need_revert = child_inheritable.is_modified(); } else { - Log::err(format!("Property {} is not inheritable!", path)) + Log::err(format!("Property {path} is not inheritable!")) } }) } Err(e) => Log::err(format!( - "Failed to resolve child path {}. Reason: {:?}", - path, e + "Failed to resolve child path {path}. Reason: {e:?}" )), }); }); @@ -541,8 +539,7 @@ pub trait SceneGraphNode: AbstractSceneNode + Clone + 'static { was_set = true; } Err(_) => Log::err(format!( - "Failed to revert property {}. Reason: no such property!", - path + "Failed to revert property {path}. Reason: no such property!" )), }, ); @@ -1150,8 +1147,7 @@ pub trait SceneGraph: BaseSceneGraph { Log::writeln( MessageKind::Information, format!( - "Integrity restored for {} instances! {} new nodes were added!", - instance_count, restored_count + "Integrity restored for {instance_count} instances! {restored_count} new nodes were added!" ), ); diff --git a/fyrox-graphics/src/error.rs b/fyrox-graphics/src/error.rs index 5b8972b44..8ca513455 100644 --- a/fyrox-graphics/src/error.rs +++ b/fyrox-graphics/src/error.rs @@ -170,7 +170,7 @@ impl From for FrameworkError { #[cfg(not(target_arch = "wasm32"))] impl From for FrameworkError { fn from(err: glutin::error::Error) -> Self { - Self::Custom(format!("{:?}", err)) + Self::Custom(format!("{err:?}")) } } @@ -182,6 +182,6 @@ impl From for FrameworkError { impl From> for FrameworkError { fn from(e: Box) -> Self { - Self::Custom(format!("{:?}", e)) + Self::Custom(format!("{e:?}")) } } diff --git a/fyrox-graphics/src/gl/program.rs b/fyrox-graphics/src/gl/program.rs index f6b742dc9..9bdf87891 100644 --- a/fyrox-graphics/src/gl/program.rs +++ b/fyrox-graphics/src/gl/program.rs @@ -74,7 +74,7 @@ unsafe fn create_shader( if !status { Log::writeln( MessageKind::Error, - format!("Failed to compile {} shader: {}", name, compilation_message), + format!("Failed to compile {name} shader: {compilation_message}"), ); Err(FrameworkError::ShaderCompilationFailed { shader_name: name, @@ -84,12 +84,9 @@ unsafe fn create_shader( let msg = if compilation_message.is_empty() || compilation_message.chars().all(|c| c.is_whitespace()) { - format!("Shader {} compiled successfully!", name) + format!("Shader {name} compiled successfully!") } else { - format!( - "Shader {} compiled successfully!\nAdditional info: {}", - name, compilation_message - ) + format!("Shader {name} compiled successfully!\nAdditional info: {compilation_message}") }; Log::writeln(MessageKind::Information, msg); @@ -310,14 +307,14 @@ impl GlProgram { unsafe { let vertex_shader = create_shader( server, - format!("{}_VertexShader", name), + format!("{name}_VertexShader"), glow::VERTEX_SHADER, vertex_source, server.gl_kind(), )?; let fragment_shader = create_shader( server, - format!("{}_FragmentShader", name), + format!("{name}_FragmentShader"), glow::FRAGMENT_SHADER, fragment_source, server.gl_kind(), @@ -334,22 +331,20 @@ impl GlProgram { if !status { Log::writeln( MessageKind::Error, - format!("Failed to link {} shader: {}", name, link_message), + format!("Failed to link {name} shader: {link_message}"), ); Err(FrameworkError::ShaderLinkingFailed { shader_name: name.to_owned(), error_message: link_message, }) } else { - let msg = - if link_message.is_empty() || link_message.chars().all(|c| c.is_whitespace()) { - format!("Shader {} linked successfully!", name) - } else { - format!( - "Shader {} linked successfully!\nAdditional info: {}", - name, link_message - ) - }; + let msg = if link_message.is_empty() + || link_message.chars().all(|c| c.is_whitespace()) + { + format!("Shader {name} linked successfully!") + } else { + format!("Shader {name} linked successfully!\nAdditional info: {link_message}") + }; Log::writeln(MessageKind::Information, msg); diff --git a/fyrox-impl/src/engine/error.rs b/fyrox-impl/src/engine/error.rs index df38c5add..8f33877ae 100644 --- a/fyrox-impl/src/engine/error.rs +++ b/fyrox-impl/src/engine/error.rs @@ -61,6 +61,6 @@ impl From for EngineError { impl From> for EngineError { fn from(e: Box) -> Self { - Self::Custom(format!("{:?}", e)) + Self::Custom(format!("{e:?}")) } } diff --git a/fyrox-impl/src/engine/executor.rs b/fyrox-impl/src/engine/executor.rs index ccc9297f5..f98374d53 100644 --- a/fyrox-impl/src/engine/executor.rs +++ b/fyrox-impl/src/engine/executor.rs @@ -231,7 +231,7 @@ impl Executor { engine.resource_manager.state().set_watcher(Some(watcher)); } Err(e) => { - Log::err(format!("Unable to create resource watcher. Reason {:?}", e)); + Log::err(format!("Unable to create resource watcher. Reason {e:?}")); } } } @@ -336,7 +336,7 @@ impl Executor { if let Err(e) = engine.set_frame_size(size.into()) { Log::writeln( MessageKind::Error, - format!("Unable to set frame size: {:?}", e), + format!("Unable to set frame size: {e:?}"), ); } } diff --git a/fyrox-impl/src/engine/mod.rs b/fyrox-impl/src/engine/mod.rs index 6c4d16d6e..66fafbbf6 100644 --- a/fyrox-impl/src/engine/mod.rs +++ b/fyrox-impl/src/engine/mod.rs @@ -2369,8 +2369,7 @@ impl Engine { .map_err(|e| e.to_string())?; Log::info(format!( - "Watching for changes in plugin {:?}...", - source_lib_path + "Watching for changes in plugin {source_lib_path:?}..." )); PluginContainer::Dynamic { @@ -2396,8 +2395,7 @@ impl Engine { self.plugins.push(plugin); Log::info(format!( - "Plugin {:?} was loaded successfully", - source_lib_path + "Plugin {source_lib_path:?} was loaded successfully" )); Ok(&**self.plugins.last().unwrap()) @@ -2654,10 +2652,7 @@ impl Engine { task_pool: &mut self.task_pool, }); - Log::info(format!( - "Plugin {} was successfully reloaded!", - plugin_index - )); + Log::info(format!("Plugin {plugin_index} was successfully reloaded!")); Ok(()) } diff --git a/fyrox-impl/src/material/mod.rs b/fyrox-impl/src/material/mod.rs index d6f92b3bb..e1da14fc2 100644 --- a/fyrox-impl/src/material/mod.rs +++ b/fyrox-impl/src/material/mod.rs @@ -575,7 +575,7 @@ impl Display for MaterialError { ) } MaterialError::Visit(e) => { - write!(f, "Failed to visit data source. Reason: {:?}", e) + write!(f, "Failed to visit data source. Reason: {e:?}") } } } @@ -918,9 +918,8 @@ impl Material { ); Log::info(format!( - "Added {} property to the material instance, since it exists in the \ - shader {}, but not in the material instance.", - name, shader_kind + "Added {name} property to the material instance, since it exists in the \ + shader {shader_kind}, but not in the material instance." )); } } @@ -936,9 +935,8 @@ impl Material { self.properties.remove(&property_name); Log::info(format!( - "Removing {} property from the material instance, since it does \ - not exists in the shader {}.", - property_name, shader_kind + "Removing {property_name} property from the material instance, since it does \ + not exists in the shader {shader_kind}." )); } } diff --git a/fyrox-impl/src/renderer/cache/shader.rs b/fyrox-impl/src/renderer/cache/shader.rs index 0eb2ecae2..1e0cb4714 100644 --- a/fyrox-impl/src/renderer/cache/shader.rs +++ b/fyrox-impl/src/renderer/cache/shader.rs @@ -61,8 +61,7 @@ impl ShaderSet { } Err(e) => { return Err(FrameworkError::Custom(format!( - "Failed to create {} shader' GPU program. Reason: {:?}", - program_name, e + "Failed to create {program_name} shader' GPU program. Reason: {e:?}" ))); } }; @@ -100,7 +99,7 @@ impl ShaderCache { ) { Ok(shader_set) => Some(shader_set), Err(error) => { - Log::err(format!("{}", error)); + Log::err(format!("{error}")); None } } diff --git a/fyrox-impl/src/renderer/cache/texture.rs b/fyrox-impl/src/renderer/cache/texture.rs index f1fa4333b..2c2f76729 100644 --- a/fyrox-impl/src/renderer/cache/texture.rs +++ b/fyrox-impl/src/renderer/cache/texture.rs @@ -112,10 +112,7 @@ impl TextureCache { ) { Log::writeln( MessageKind::Error, - format!( - "Unable to upload new texture data to GPU. Reason: {:?}", - e - ), + format!("Unable to upload new texture data to GPU. Reason: {e:?}"), ) } else { entry.modifications_counter = modifications_count; diff --git a/fyrox-impl/src/renderer/mod.rs b/fyrox-impl/src/renderer/mod.rs index a71059295..db12d1385 100644 --- a/fyrox-impl/src/renderer/mod.rs +++ b/fyrox-impl/src/renderer/mod.rs @@ -1302,7 +1302,7 @@ impl Renderer { Err(e) => { Log::writeln( MessageKind::Error, - format!("Failed to upload texture to GPU. Reason: {:?}", e), + format!("Failed to upload texture to GPU. Reason: {e:?}"), ); } } @@ -1397,8 +1397,7 @@ impl Renderer { let height = frame_size.y as usize; Log::info(format!( - "A new associated scene rendering data was created for scene {}!", - scene_handle + "A new associated scene rendering data was created for scene {scene_handle}!" )); AssociatedSceneData::new(server, width, height).unwrap() diff --git a/fyrox-impl/src/resource/fbx/document/attribute.rs b/fyrox-impl/src/resource/fbx/document/attribute.rs index 957534ce0..f444e291b 100644 --- a/fyrox-impl/src/resource/fbx/document/attribute.rs +++ b/fyrox-impl/src/resource/fbx/document/attribute.rs @@ -33,13 +33,13 @@ pub enum FbxAttribute { impl std::fmt::Display for FbxAttribute { fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> { match self { - FbxAttribute::Double(double) => write!(f, "{}", double), - FbxAttribute::Float(float) => write!(f, "{}", float), - FbxAttribute::Integer(integer) => write!(f, "{}", integer), - FbxAttribute::Long(long) => write!(f, "{}", long), - FbxAttribute::Bool(boolean) => write!(f, "{}", boolean), - FbxAttribute::String(string) => write!(f, "{}", string), - FbxAttribute::RawData(raw) => write!(f, "{:?}", raw), + FbxAttribute::Double(double) => write!(f, "{double}"), + FbxAttribute::Float(float) => write!(f, "{float}"), + FbxAttribute::Integer(integer) => write!(f, "{integer}"), + FbxAttribute::Long(long) => write!(f, "{long}"), + FbxAttribute::Bool(boolean) => write!(f, "{boolean}"), + FbxAttribute::String(string) => write!(f, "{string}"), + FbxAttribute::RawData(raw) => write!(f, "{raw:?}"), } } } @@ -54,7 +54,7 @@ impl FbxAttribute { FbxAttribute::Bool(val) => Ok(*val as i32), FbxAttribute::String(val) => match val.parse::() { Ok(i) => Ok(i), - Err(_) => Err(format!("Unable to convert string {} to i32", val)), + Err(_) => Err(format!("Unable to convert string {val} to i32")), }, FbxAttribute::RawData(_) => Err("Unable to convert raw data to i32".to_string()), } @@ -69,7 +69,7 @@ impl FbxAttribute { FbxAttribute::Bool(val) => Ok(*val as i64), FbxAttribute::String(val) => match val.parse::() { Ok(i) => Ok(i), - Err(_) => Err(format!("Unable to convert string {} to i64", val)), + Err(_) => Err(format!("Unable to convert string {val} to i64")), }, FbxAttribute::RawData(_) => Err("Unable to convert raw data to i64".to_string()), } @@ -84,7 +84,7 @@ impl FbxAttribute { FbxAttribute::Bool(val) => Ok((*val as i64) as f64), FbxAttribute::String(val) => match val.parse::() { Ok(i) => Ok(i), - Err(_) => Err(format!("Unable to convert string {} to f64", val)), + Err(_) => Err(format!("Unable to convert string {val} to f64")), }, FbxAttribute::RawData(_) => Err("Unable to convert raw data to f64".to_string()), } @@ -99,7 +99,7 @@ impl FbxAttribute { FbxAttribute::Bool(val) => Ok((*val as i32) as f32), FbxAttribute::String(val) => match val.parse::() { Ok(i) => Ok(i), - Err(_) => Err(format!("Unable to convert string {} to f32", val)), + Err(_) => Err(format!("Unable to convert string {val} to f32")), }, FbxAttribute::RawData(_) => Err("Unable to convert raw data to f32".to_string()), } diff --git a/fyrox-impl/src/resource/fbx/document/mod.rs b/fyrox-impl/src/resource/fbx/document/mod.rs index 6e9c447fc..0c2a4da17 100644 --- a/fyrox-impl/src/resource/fbx/document/mod.rs +++ b/fyrox-impl/src/resource/fbx/document/mod.rs @@ -64,8 +64,7 @@ impl FbxNode { match self.attributes.get(n) { Some(attrib) => Ok(attrib), None => Err(format!( - "Unable to get {} attribute because index out of bounds.", - n + "Unable to get {n} attribute because index out of bounds." )), } } @@ -106,7 +105,7 @@ impl FbxNodeContainer { } } - Err(format!("FBX DOM: Unable to find {} node", name)) + Err(format!("FBX DOM: Unable to find {name} node")) } /// Searches node by specified name and borrows a reference to it @@ -123,7 +122,7 @@ impl FbxNodeContainer { } } - Err(format!("FBX DOM: Unable to find {} node", name)) + Err(format!("FBX DOM: Unable to find {name} node")) } pub fn get(&self, handle: Handle) -> &FbxNode { diff --git a/fyrox-impl/src/resource/fbx/mod.rs b/fyrox-impl/src/resource/fbx/mod.rs index 49ac3f25d..ceed59a20 100644 --- a/fyrox-impl/src/resource/fbx/mod.rs +++ b/fyrox-impl/src/resource/fbx/mod.rs @@ -322,10 +322,7 @@ async fn create_surfaces( { Log::writeln( MessageKind::Error, - format!( - "Failed to set diffuseColor property for material. Reason: {:?}", - e, - ), + format!("Failed to set diffuseColor property for material. Reason: {e:?}",), ) } @@ -446,9 +443,8 @@ async fn create_surfaces( Log::writeln( MessageKind::Error, format!( - "Unable to set material property {}\ - for FBX material! Reason: {:?}", - property_name, e + "Unable to set material property {property_name}\ + for FBX material! Reason: {e:?}" ), ); } @@ -457,8 +453,7 @@ async fn create_surfaces( Log::writeln( MessageKind::Warning, format!( - "Unable to find a texture {:?} for 3D model {:?} using {:?} option!", - filename, model_path, model_import_options + "Unable to find a texture {filename:?} for 3D model {model_path:?} using {model_import_options:?} option!" ), ); } diff --git a/fyrox-impl/src/resource/fbx/scene/light.rs b/fyrox-impl/src/resource/fbx/scene/light.rs index 1a223fbcd..9ff874322 100644 --- a/fyrox-impl/src/resource/fbx/scene/light.rs +++ b/fyrox-impl/src/resource/fbx/scene/light.rs @@ -95,10 +95,7 @@ impl FbxLight { _ => { Log::writeln( MessageKind::Warning, - format!( - "FBX: Unknown light type {}, fallback to Point!", - type_code - ), + format!("FBX: Unknown light type {type_code}, fallback to Point!"), ); FbxLightType::Point } diff --git a/fyrox-impl/src/resource/model/mod.rs b/fyrox-impl/src/resource/model/mod.rs index 6445452fe..ebcd2a04a 100644 --- a/fyrox-impl/src/resource/model/mod.rs +++ b/fyrox-impl/src/resource/model/mod.rs @@ -181,8 +181,7 @@ impl<'a> InstantiationContext<'a> { node.instance_id = *id; } else { Log::warn(format!( - "No id specified for node {}! Random id will be used.", - original_handle + "No id specified for node {original_handle}! Random id will be used." )) } } @@ -761,8 +760,7 @@ impl Model { // TODO: Add more formats. _ => { return Err(ModelLoadError::NotSupported(format!( - "Unsupported model resource format: {}", - extension + "Unsupported model resource format: {extension}" ))) } }; diff --git a/fyrox-impl/src/resource/texture/mod.rs b/fyrox-impl/src/resource/texture/mod.rs index b949e62ee..5946aceb0 100644 --- a/fyrox-impl/src/resource/texture/mod.rs +++ b/fyrox-impl/src/resource/texture/mod.rs @@ -197,8 +197,7 @@ impl Visit for TextureKind { }, _ => { return VisitResult::Err(VisitError::User(format!( - "Invalid texture kind {}!", - id + "Invalid texture kind {id}!" ))) } }; @@ -974,7 +973,7 @@ impl TexturePixelKind { 22 => Ok(Self::RGB16F), 23 => Ok(Self::R32F), 24 => Ok(Self::R16F), - _ => Err(format!("Invalid texture kind {}!", id)), + _ => Err(format!("Invalid texture kind {id}!")), } } diff --git a/fyrox-impl/src/scene/base.rs b/fyrox-impl/src/scene/base.rs index e3a043ce6..8302a881a 100644 --- a/fyrox-impl/src/scene/base.rs +++ b/fyrox-impl/src/scene/base.rs @@ -1046,8 +1046,7 @@ pub(crate) fn visit_opt_script( .try_create(&script_type_uuid) .ok_or_else(|| { VisitError::User(format!( - "There is no corresponding script constructor for {} type!", - script_type_uuid + "There is no corresponding script constructor for {script_type_uuid} type!" )) })?, ) diff --git a/fyrox-impl/src/scene/graph/mod.rs b/fyrox-impl/src/scene/graph/mod.rs index 6d06074d4..3bfe569d6 100644 --- a/fyrox-impl/src/scene/graph/mod.rs +++ b/fyrox-impl/src/scene/graph/mod.rs @@ -725,8 +725,7 @@ impl Graph { Log::writeln( MessageKind::Error, format!( - "Failed to apply light map texture to material. Reason {:?}", - e + "Failed to apply light map texture to material. Reason {e:?}" ), ) } @@ -789,8 +788,7 @@ impl Graph { Log::writeln( MessageKind::Error, format!( - "Failed to apply light map texture to material. Reason {:?}", - e + "Failed to apply light map texture to material. Reason {e:?}" ), ) } diff --git a/fyrox-impl/src/scene/mesh/surface.rs b/fyrox-impl/src/scene/mesh/surface.rs index 1fc7a551a..ea6a0296c 100644 --- a/fyrox-impl/src/scene/mesh/surface.rs +++ b/fyrox-impl/src/scene/mesh/surface.rs @@ -340,8 +340,7 @@ impl SurfaceData { if v1 == v2 || v1 == v3 || v2 == v3 { Log::warn(format!( "Degenerated triangle found when calculating tangents. Lighting may be \ - incorrect! Triangle indices: {:?}. Triangle vertices: {v1} {v2} {v3}", - triangle, + incorrect! Triangle indices: {triangle:?}. Triangle vertices: {v1} {v2} {v3}", )); } diff --git a/fyrox-impl/src/scene/mod.rs b/fyrox-impl/src/scene/mod.rs index 53bd8a434..e3d7b7437 100644 --- a/fyrox-impl/src/scene/mod.rs +++ b/fyrox-impl/src/scene/mod.rs @@ -356,16 +356,14 @@ impl SceneLoader { let used_resources_count = used_resources.len(); Log::info(format!( - "SceneLoader::finish() - {} resources collected. Waiting them to load...", - used_resources_count + "SceneLoader::finish() - {used_resources_count} resources collected. Waiting them to load..." )); // Wait everything. join_all(used_resources.into_iter()).await; Log::info(format!( - "SceneLoader::finish() - All {} resources have finished loading.", - used_resources_count + "SceneLoader::finish() - All {used_resources_count} resources have finished loading." )); // TODO: Move into Camera::restore_resources? diff --git a/fyrox-impl/src/scene/node/container.rs b/fyrox-impl/src/scene/node/container.rs index 24a9323d5..f38940259 100644 --- a/fyrox-impl/src/scene/node/container.rs +++ b/fyrox-impl/src/scene/node/container.rs @@ -69,8 +69,7 @@ fn read_node(name: &str, visitor: &mut Visitor) -> Result { 2 => Node::new(DirectionalLight::default()), _ => { return Err(VisitError::User(format!( - "Invalid legacy light kind {}", - light_id + "Invalid legacy light kind {light_id}" ))) } }; @@ -96,8 +95,7 @@ fn read_node(name: &str, visitor: &mut Visitor) -> Result { 16 => Node::new(Listener::default()), _ => { return Err(VisitError::User(format!( - "Invalid legacy node kind {}", - kind_id + "Invalid legacy node kind {kind_id}" ))) } }; @@ -120,7 +118,7 @@ fn read_node(name: &str, visitor: &mut Visitor) -> Result { let mut node = serialization_context .node_constructors .try_create(&id) - .ok_or_else(|| VisitError::User(format!("Unknown node type uuid {}!", id)))?; + .ok_or_else(|| VisitError::User(format!("Unknown node type uuid {id}!")))?; node.visit("NodeData", &mut region)?; diff --git a/fyrox-impl/src/scene/particle_system/emitter/mod.rs b/fyrox-impl/src/scene/particle_system/emitter/mod.rs index a7c9dc213..1af4ee7aa 100644 --- a/fyrox-impl/src/scene/particle_system/emitter/mod.rs +++ b/fyrox-impl/src/scene/particle_system/emitter/mod.rs @@ -66,7 +66,7 @@ impl Emitter { 1 => Ok(Self::Cuboid(Default::default())), 2 => Ok(Self::Sphere(Default::default())), 3 => Ok(Self::Cylinder(Default::default())), - _ => Err(format!("Invalid emitter id {}!", id)), + _ => Err(format!("Invalid emitter id {id}!")), } } diff --git a/fyrox-impl/src/scene/sound/context.rs b/fyrox-impl/src/scene/sound/context.rs index 8d126164d..c9015625c 100644 --- a/fyrox-impl/src/scene/sound/context.rs +++ b/fyrox-impl/src/scene/sound/context.rs @@ -152,10 +152,7 @@ impl SoundContext { if state.is_valid_handle(sound) { state.remove_source(sound); - Log::info(format!( - "Native sound source was removed for node: {}", - name - )); + Log::info(format!("Native sound source was removed for node: {name}")); } } diff --git a/fyrox-impl/src/scene/sound/mod.rs b/fyrox-impl/src/scene/sound/mod.rs index 5e4b8a2e7..da0597187 100644 --- a/fyrox-impl/src/scene/sound/mod.rs +++ b/fyrox-impl/src/scene/sound/mod.rs @@ -445,7 +445,7 @@ impl NodeTrait for Sound { None => Err("Sound buffer is failed to load, the reason is unknown!" .to_string()), Some(err) => { - Err(format!("Sound buffer is failed to load. Reason: {:?}", err)) + Err(format!("Sound buffer is failed to load. Reason: {err:?}")) } } } diff --git a/fyrox-impl/src/scene/terrain/brushstroke/mod.rs b/fyrox-impl/src/scene/terrain/brushstroke/mod.rs index a97834802..a34970c48 100644 --- a/fyrox-impl/src/scene/terrain/brushstroke/mod.rs +++ b/fyrox-impl/src/scene/terrain/brushstroke/mod.rs @@ -1,1049 +1,1045 @@ -// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -//! The brushstroke module contains tools for modifying terrain textures. -//! It uses a triple-buffer system to separate the UI mouse movements -//! from the update of the data within the actual textures. -//! 1. The first buffer is a [std::sync::mpsc::channel] that is used to send -//! messages to control a thread that processes brush strokes. -//! These messages are [BrushThreadMessage]. Some of the messages -//! are processed as soon as they are received, but [BrushThreadMessage::Pixel] -//! messages are sent to the next buffer. -//! 2. The pixel message buffer holds a limited number of pixel messages. -//! It serves to merge redundent pixel messages to spare the thread from -//! repeating work. It is expected that brush operations will paint multiple -//! times to the same pixel in quick succession. -//! Once the new value for a pixel has been calculated, the value is stored -//! in the third buffer. -//! 3. The [StrokeData] buffer stores every change that a particular brush stroke -//! has made to the texture. Because modifying a texture is a nontrivial operation, -//! modified pixels are allowed to accumulate to some quantity before the new pixel -//! values are actually written to the textures of the terrain. -//! [StrokeChunks] is used to keep track of which pixels are waiting to be written -//! to which terrain chunks. -use super::Chunk; -use crate::asset::ResourceDataRef; -use crate::core::{ - algebra::{Matrix2, Vector2}, - log::Log, - math::Rect, - pool::Handle, - reflect::prelude::*, -}; -use crate::fxhash::FxHashMap; -use crate::resource::texture::{Texture, TextureResource}; -use crate::scene::node::Node; -use fyrox_core::uuid_provider; -use std::collections::VecDeque; -use std::sync::mpsc::{Receiver, SendError, Sender}; - -pub mod brushraster; -use brushraster::*; -pub mod strokechunks; -use strokechunks::*; - -/// The number of pixel messages we can accept at once before we must start processing them. -/// Often later messages will cause earlier messages to be unnecessary, so it can be more efficient -/// to let some messages accumulate rather than process each message one-at-a-time. -const MESSAGE_BUFFER_SIZE: usize = 40; -/// The number of processed pixels we can hold before we must write the pixels to the targetted textures. -/// Modifying a texture is expensive, so it is important to do it in batches of multiple pixels. -const PIXEL_BUFFER_SIZE: usize = 40; -/// The maximum number of pixels that are allowed to be involved in a single step of a brushstroke. -/// This limit is arbitrarily chosen, but there should be some limit to prevent the editor -/// from freezing as a result of an excessively large brush. -const BRUSH_PIXEL_SANITY_LIMIT: i32 = 1000000; - -#[inline] -fn mask_raise(original: u8, amount: f32) -> u8 { - (original as f32 + amount * 255.0).clamp(0.0, 255.0) as u8 -} - -#[inline] -fn mask_lerp(original: u8, value: f32, t: f32) -> u8 { - let original = original as f32; - let value = value * 255.0; - (original * (1.0 - t) + value * t).clamp(0.0, 255.0) as u8 -} - -/// A message that can be sent to the terrain painting thread to control the painting. -#[derive(Debug, Clone)] -pub enum BrushThreadMessage { - /// Set the brush that will be used for future pixels and the textures that will be modified. - StartStroke(Brush, Handle, TerrainTextureData), - /// No futher pixels will be sent for the current stroke. - EndStroke, - /// Paint the given pixel as part of the current stroke. - Pixel(BrushPixelMessage), -} - -/// A message that can be sent to indicate that the pixel at the given coordinates -/// should be painted with the given alpha. -#[derive(Debug, Clone)] -pub struct BrushPixelMessage { - /// The coordinates of the pixel to paint. - pub position: Vector2, - /// The transparency of the brush, from 0.0 for transparent to 1.0 for opaque. - pub alpha: f32, - /// A value whose meaning depends on the brush. - /// For flatten brushes, this is the target height. - pub value: f32, -} - -/// A queue that stores pixels that are waiting to be drawn by the brush. -pub struct PixelMessageBuffer { - data: VecDeque, - max_size: usize, -} - -impl PixelMessageBuffer { - /// Create a new buffer with the given size. - #[inline] - pub fn new(max_size: usize) -> Self { - Self { - data: VecDeque::with_capacity(max_size), - max_size, - } - } - /// True if the buffer has reached its maximum size. - #[inline] - pub fn is_full(&self) -> bool { - self.max_size == self.data.len() - } - /// True if there is nothing to pop from the queue. - #[inline] - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } - /// Remove the message from the front of the queue and return it, if the queue is not empty. - #[inline] - pub fn pop(&mut self) -> Option { - self.data.pop_front() - } - /// Push a message onto the back of the queue, or panic of the queue is full. - pub fn push(&mut self, message: BrushPixelMessage) { - assert!(self.data.len() < self.max_size); - if let Some(m) = self - .data - .iter_mut() - .find(|m| m.position == message.position) - { - if message.alpha > m.alpha { - m.alpha = message.alpha; - m.value = message.value; - } - } else { - self.data.push_back(message); - } - } -} - -/// Object to send to painting thread to control which textures are modified. -#[derive(Debug, Clone)] -pub struct TerrainTextureData { - /// The height and width of the texture in pixels. - pub chunk_size: Vector2, - /// The kind of texture. - pub kind: TerrainTextureKind, - /// The texture resources, organized by chunk grid position. - pub resources: FxHashMap, TextureResource>, -} - -/// Terrain textures come in multiple kinds. -/// Height textures contain f32 values for each vertex of the terrain. -/// Mask textures contain u8 values indicating transparency. -/// Coordinates are interpreted differently between the two kinds of texture -/// because the data in height textures overlaps with the data in neighboring chunks, -/// so the pixels along each edge are duplicated and must be kept in sync -/// so that the chunks do not disconnect. -#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] -pub enum TerrainTextureKind { - #[default] - /// Height texture with f32 height values and overlapping edges between chunks. - Height, - /// Mask texture with u8 oppacity values. - Mask, -} - -/// Sender with methods for sending the messages which control a brush painting thread. -pub struct BrushSender(Sender); - -impl BrushSender { - /// Create a new BrushSender using the given Sender. - pub fn new(sender: Sender) -> Self { - Self(sender) - } - /// Begin a new stroke using the given brush. - pub fn start_stroke(&self, brush: Brush, node: Handle, data: TerrainTextureData) { - self.0 - .send(BrushThreadMessage::StartStroke(brush, node, data)) - .unwrap_or_else(on_send_failure); - } - /// End the current stroke. - pub fn end_stroke(&self) { - self.0 - .send(BrushThreadMessage::EndStroke) - .unwrap_or_else(on_send_failure); - } - /// Draw a pixel using the brush that was set in the most recent call to [BrushSender::start_stroke]. - #[inline] - pub fn draw_pixel(&self, position: Vector2, alpha: f32, value: f32) { - if alpha == 0.0 { - return; - } - self.0 - .send(BrushThreadMessage::Pixel(BrushPixelMessage { - position, - alpha, - value, - })) - .unwrap_or_else(on_send_failure); - } -} - -fn on_send_failure(error: SendError) { - Log::err(format!( - "A brush painting message was not sent. {:?}", - error - )); -} - -/// Type for a callback that delivers the original data of textures that have been modified -/// by the brush so that changes might be undone. -pub type UndoChunkHandler = dyn FnMut(UndoData) + Send; - -/// A record of original data data for chunks that have been modified by a brushstroke. -pub struct UndoData { - /// The handle of the terrain being edited - pub node: Handle, - /// The data of the chunks as they were before the brushstroke. - pub chunks: Vec, - /// The kind of data within the terrain that is being edited. - pub target: BrushTarget, -} - -#[derive(Default)] -/// Data for an in-progress terrain painting operation -pub struct BrushStroke { - /// The brush that is currently being used. This determines how the terrain textures are edited. - brush: Brush, - /// The textures for the terrain that is currently being edited. - textures: FxHashMap, TextureResource>, - /// The node of the terrain being edted - node: Handle, - /// Callback to handle the saved original chunk data after each stroke. - /// This is called when [BrushThreadMessage::EndStroke] is received. - undo_chunk_handler: Option>, - /// A record of which pixels have been modified in each chunk since the last UpdateTextures. - /// This is cleared after [BrushThreadMessage::UpdateTextures] is received, - /// when the pixel data is transferred into the textures. - chunks: StrokeChunks, - /// Data copied from chunks that have been edited by the current brush stroke. - /// This preserves the textures as they were before the stroke began, so that - /// an undo command can be created at the end of the stroke. - undo_chunks: Vec, - /// A record of every pixel of the stroke, including the strength of the brush at that pixel, - /// the original value before the stroke began, and the current value. - height_pixels: StrokeData, - /// A record of every pixel of the stroke, including the strength of the brush at that pixel, - /// the original value before the stroke began, and the current value. - mask_pixels: StrokeData, -} - -/// Stores pixels that have been modified by a brush during a stroke. -/// It remembers the strength of the brush, the value of the painted pixel, -/// and the value of the original pixel before the brushstroke. -/// This should be cleared after each stroke using [StrokeData::clear]. -/// -/// `V` is the type of data stored in the pixel being edited. -#[derive(Debug, Default)] -pub struct StrokeData(FxHashMap, StrokeElement>); - -/// A single pixel data of a brush stroke -#[derive(Debug, Copy, Clone)] -pub struct StrokeElement { - /// The intensity of the brush stroke, with 0.0 indicating a pixel that brush has not touched - /// and 1.0 indicates a pixel fully covered by the brush. - pub strength: f32, - /// The value of the pixel before the stroke began. - pub original_value: V, - /// The current value of the pixel. - pub latest_value: V, -} - -impl BrushStroke { - /// Create a BrushStroke with the given handler for saving undo data for chunks. - pub fn with_chunk_handler(undo_chunk_handler: Box) -> Self { - Self { - undo_chunk_handler: Some(undo_chunk_handler), - ..Default::default() - } - } - /// The brush that this stroke is using. This is immutable access only, because - /// the brush's target may only be changed through [BrushStroke::start_stroke] or - /// [BrushStroke::accept_messages]. - /// - /// Mutable access to the brush's other properties is available through - /// [BrushStroke::shape], [BrushStroke::mode], [BrushStroke::hardness], - /// and [BrushStroke::alpha]. - pub fn brush(&self) -> &Brush { - &self.brush - } - /// Mutable access to the brush's shape - pub fn shape(&mut self) -> &mut BrushShape { - &mut self.brush.shape - } - /// Mutable access to the brush's mode - pub fn mode(&mut self) -> &mut BrushMode { - &mut self.brush.mode - } - /// Mutable access to the brush's hardness. The hardness controls how the edges - /// of the brush are blended with the original value of the texture. - pub fn hardness(&mut self) -> &mut f32 { - &mut self.brush.hardness - } - /// Mutable access to the brush's alpha. The alpha value controls how - /// the operation's result is blended with the original value of the texture. - pub fn alpha(&mut self) -> &mut f32 { - &mut self.brush.alpha - } - /// Insert a stamp of the brush at the given position with the given texture scale and the given value. - /// - `position`: The center of the stamp. - /// - `scale`: The size of each pixel in 2D local space. This is used to convert the brush's shape from local space to texture space. - /// - `value`: A value that is a parameter for the brush operation. - pub fn stamp(&mut self, position: Vector2, scale: Vector2, value: f32) { - let brush = self.brush.clone(); - brush.stamp(position, scale, |position, alpha| { - self.draw_pixel(BrushPixelMessage { - position, - alpha, - value, - }) - }); - } - /// Insert a smear of the brush at the given position with the given texture scale and the given value. - /// - `start`: The center of the smear's start. - /// - `end`: The center of the smear's end. - /// - `scale`: The size of each pixel in 2D local space. This is used to convert the brush's shape from local space to texture space. - /// - `value`: A value that is a parameter for the brush operation. - pub fn smear( - &mut self, - start: Vector2, - end: Vector2, - scale: Vector2, - value: f32, - ) { - let brush = self.brush.clone(); - brush.smear(start, end, scale, |position, alpha| { - self.draw_pixel(BrushPixelMessage { - position, - alpha, - value, - }) - }); - } - /// Prepare this object for a new brushstroke. - pub fn clear(&mut self) { - self.height_pixels.clear(); - self.mask_pixels.clear(); - self.chunks.clear(); - } - /// Access the data in the textures to find the value for the pixel at the given position. - pub fn data_pixel(&self, position: Vector2) -> Option - where - V: Clone, - { - // Determine which texture holds the data for the position. - let grid_pos = self.chunks.pixel_position_to_grid_position(position); - // Determine which pixel within the texture corresponds to the given position. - let origin = self.chunks.chunk_to_origin(grid_pos); - let p = position - origin; - // Access the texture and extract the data. - let texture = self.textures.get(&grid_pos)?; - let index = self.chunks.pixel_index(p); - let data = texture.data_ref(); - Some(data.data_of_type::().unwrap()[index].clone()) - } - /// Block on the given receiver until its messages are exhausted and perform the painting - /// operations according to the messages. - /// It does not return until the receiver's channel no longer has senders. - pub fn accept_messages(&mut self, receiver: Receiver) { - let mut message_buffer = PixelMessageBuffer::new(MESSAGE_BUFFER_SIZE); - loop { - // Collect all waiting messages, until the buffer is full. - while !message_buffer.is_full() { - if let Ok(message) = receiver.try_recv() { - // Act on the message, potentially adding it to the message buffer. - self.handle_message(message, &mut message_buffer); - } else { - break; - } - } - if let Some(pixel) = message_buffer.pop() { - // Perform the drawing operation for the current pixel message. - self.handle_pixel_message(pixel); - } else if self.chunks.count() > 0 { - // We have run out of pixels to process, so before we block to wait for more, - // write the currently processed pixels to the terrain textures. - self.flush(); - } else { - // If the message buffer is empty, we cannot proceed, so block until a message is available. - // Block until either a message arrives or the channel is closed. - if let Ok(message) = receiver.recv() { - // Act on the message, potentially adding it to the message buffer. - self.handle_message(message, &mut message_buffer); - } else { - // The message buffer is empty and the channel is closed, so we're finished. - // Flush pixels to the textures. - self.end_stroke(); - return; - } - } - } - } - fn handle_message( - &mut self, - message: BrushThreadMessage, - message_buffer: &mut PixelMessageBuffer, - ) { - match message { - BrushThreadMessage::StartStroke(brush, node, textures) => { - self.brush = brush; - self.node = node; - self.textures = textures.resources; - self.chunks.set_layout(textures.kind, textures.chunk_size); - } - BrushThreadMessage::EndStroke => { - // The stroke has ended, so finish processing all buffered pixel messages. - while let Some(p) = message_buffer.pop() { - self.handle_pixel_message(p); - } - // Apply buffered pixels to the terrain textures. - self.end_stroke(); - } - BrushThreadMessage::Pixel(pixel) => { - message_buffer.push(pixel); - } - } - } - /// -`brush`: The brush to paint with - /// -`node`: The handle of the terrain being modified. It is not used except to pass to `undo_chunk_handler`, - /// so it can be safely [Handle::NONE] if `undo_chunk_handler` is None, or if `undo_chunk_handler` is prepared for NONE. - /// -`textures`: Hash map of texture resources that this stroke will edit. - pub fn start_stroke(&mut self, brush: Brush, node: Handle, textures: TerrainTextureData) { - self.brush = brush; - self.node = node; - self.chunks.set_layout(textures.kind, textures.chunk_size); - self.textures = textures.resources; - } - /// Send the textures that have been touched by the brush to the undo handler, - /// then write the current changes to the textures and clear the stroke to prepare - /// for starting a new stroke. - pub fn end_stroke(&mut self) { - if let Some(handler) = &mut self.undo_chunk_handler { - // Copy the textures that are about to be modified so that the modifications can be undone. - self.chunks - .copy_texture_data(&self.textures, &mut self.undo_chunks); - // Send the saved textures to the handler so that an undo command might be created. - handler(UndoData { - node: self.node, - chunks: std::mem::take(&mut self.undo_chunks), - target: self.brush.target, - }); - } - // Flush pixels to the terrain textures - self.apply(); - self.clear(); - } - /// Insert a pixel with the given texture-space coordinates and strength. - pub fn draw_pixel(&mut self, pixel: BrushPixelMessage) { - let pixel = BrushPixelMessage { - alpha: 0.5 * (1.0 - (pixel.alpha * std::f32::consts::PI).cos()), - ..pixel - }; - let position = pixel.position; - match self.chunks.kind() { - TerrainTextureKind::Height => self.accept_pixel_height(pixel), - TerrainTextureKind::Mask => self.accept_pixel_mask(pixel), - } - self.chunks.write(position); - } - fn handle_pixel_message(&mut self, pixel: BrushPixelMessage) { - self.draw_pixel(pixel); - if self.chunks.count() >= PIXEL_BUFFER_SIZE { - self.flush(); - } - } - fn smooth_height( - &self, - position: Vector2, - kernel_radius: u32, - original: f32, - alpha: f32, - ) -> f32 { - let radius = kernel_radius as i32; - let diameter = kernel_radius * 2 + 1; - let area = (diameter * diameter - 1) as f32; - let mut total = 0.0; - for x in -radius..=radius { - for y in -radius..=radius { - if x == 0 && y == 0 { - continue; - } - let pos = position + Vector2::new(x, y); - let value = self - .height_pixels - .original_pixel_value(pos) - .copied() - .or_else(|| self.data_pixel(position)) - .unwrap_or_default(); - total += value; - } - } - let smoothed = total / area; - original * (1.0 - alpha) + smoothed * alpha - } - fn smooth_mask( - &self, - position: Vector2, - kernel_radius: u32, - original: u8, - alpha: f32, - ) -> u8 { - let radius = kernel_radius as i32; - let diameter = kernel_radius as u64 * 2 + 1; - let area = diameter * diameter - 1; - let mut total: u64 = 0; - for x in -radius..=radius { - for y in -radius..=radius { - if x == 0 && y == 0 { - continue; - } - let pos = position + Vector2::new(x, y); - let value = self - .mask_pixels - .original_pixel_value(pos) - .copied() - .or_else(|| self.data_pixel(position)) - .unwrap_or_default(); - total += value as u64; - } - } - let smoothed = total / area; - (original as f32 * (1.0 - alpha) + smoothed as f32 * alpha).clamp(0.0, 255.0) as u8 - } - fn accept_pixel_height(&mut self, pixel: BrushPixelMessage) { - let position = pixel.position; - let mut pixels = std::mem::take(&mut self.height_pixels); - let original = pixels.update_strength(position, pixel.alpha, || self.data_pixel(position)); - self.height_pixels = pixels; - let Some(original) = original else { - return; - }; - let alpha = self.brush.alpha * pixel.alpha; - let result: f32 = match self.brush.mode { - BrushMode::Raise { amount } => original + amount * alpha, - BrushMode::Flatten => original * (1.0 - alpha) + pixel.value * alpha, - BrushMode::Assign { value } => original * (1.0 - alpha) + value * alpha, - BrushMode::Smooth { kernel_radius } => { - self.smooth_height(position, kernel_radius, original, alpha) - } - }; - self.height_pixels.set_latest(position, result); - } - fn accept_pixel_mask(&mut self, pixel: BrushPixelMessage) { - let position = pixel.position; - let mut pixels = std::mem::take(&mut self.mask_pixels); - let original = pixels.update_strength(position, pixel.alpha, || self.data_pixel(position)); - self.mask_pixels = pixels; - let Some(original) = original else { - return; - }; - let alpha = self.brush.alpha * pixel.alpha; - let result: u8 = match self.brush.mode { - BrushMode::Raise { amount } => mask_raise(original, amount * alpha), - BrushMode::Flatten => mask_lerp(original, pixel.value, alpha), - BrushMode::Assign { value } => mask_lerp(original, value, alpha), - BrushMode::Smooth { kernel_radius } => { - self.smooth_mask(position, kernel_radius, original, alpha) - } - }; - self.mask_pixels.set_latest(position, result); - } - /// Update the texture resources to match the current state of this stroke. - pub fn flush(&mut self) { - // chunks stores the pixels that we have modified but not yet written to the textures. - // If we have an undo handler, we must inform the handler of the textures we are writing to - // because we are about to forget that we have written to them. - if self.undo_chunk_handler.is_some() { - // Copy the textures that are about to be modified so that the modifications can be undone. - self.chunks - .copy_texture_data(&self.textures, &mut self.undo_chunks); - } - // Do the actual texture modification. - self.apply(); - // Erase our memory of having modified these pixels, since they have already been finalized - // by writing them to the textures. - self.chunks.clear(); - } - fn apply(&self) { - match self.chunks.kind() { - TerrainTextureKind::Height => { - self.chunks.apply(&self.height_pixels, &self.textures); - } - TerrainTextureKind::Mask => { - self.chunks.apply(&self.mask_pixels, &self.textures); - } - } - } -} - -impl StrokeData { - /// Reset the brush stroke so it is ready to begin a new stroke. - #[inline] - pub fn clear(&mut self) { - self.0.clear() - } - /// For every pixel that is modified by the stroke, the original values - /// is stored as it was before the stroke began. - #[inline] - pub fn original_pixel_value(&self, position: Vector2) -> Option<&V> { - self.0.get(&position).map(|x| &x.original_value) - } - /// The updated pixel value based on whatever editing operation the stroke is performing. - #[inline] - pub fn latest_pixel_value(&self, position: Vector2) -> Option<&V> { - self.0.get(&position).map(|x| &x.latest_value) - } - /// Update the stroke with a new value at the given pixel position. - /// This must only be called after calling [StrokeData::update_strength] - /// to ensure that this stroke contains data for the position. - /// Otherwise this method may panic. - #[inline] - pub fn set_latest(&mut self, position: Vector2, value: V) { - if let Some(el) = self.0.get_mut(&position) { - el.latest_value = value; - } else { - panic!("Setting latest value of missing element"); - } - } - /// Stores or modifies the StrokeElement at the given position. - /// If the element is updated, return the original pixel value of the element. - /// - `position`: The position of the data to modify within the terrain. - /// - `strength`: The strength of the brush at the position, from 0.0 to 1.0. - /// The element is updated if the stored strength is less than the given strength. - /// If there is no stored strength, that is treated as a strength of 0.0. - /// - `pixel_value`: The current value of the data. - /// This may be stored in the StrokeData if no pixel value is currently recorded for the given position. - /// Otherwise, this value is ignored. - #[inline] - pub fn update_strength( - &mut self, - position: Vector2, - strength: f32, - pixel_value: F, - ) -> Option - where - V: Clone, - F: FnOnce() -> Option, - { - if strength == 0.0 { - None - } else if let Some(element) = self.0.get_mut(&position) { - if element.strength < strength { - element.strength = strength; - Some(element.original_value.clone()) - } else { - None - } - } else { - let value = pixel_value()?; - let element = StrokeElement { - strength, - latest_value: value.clone(), - original_value: value.clone(), - }; - self.0.insert(position, element); - Some(value) - } - } -} - -/// Shape of a brush. -#[derive(Copy, Clone, Reflect, Debug)] -pub enum BrushShape { - /// Circle with given radius. - Circle { - /// Radius of the circle. - radius: f32, - }, - /// Rectangle with given width and height. - Rectangle { - /// Width of the rectangle. - width: f32, - /// Length of the rectangle. - length: f32, - }, -} - -uuid_provider!(BrushShape = "a4dbfba0-077c-4658-9972-38384a8432f9"); - -impl Default for BrushShape { - fn default() -> Self { - BrushShape::Circle { radius: 1.0 } - } -} - -impl BrushShape { - /// Return true if the given point is within the shape when positioned at the given center point. - pub fn contains(&self, brush_center: Vector2, pixel_position: Vector2) -> bool { - match *self { - BrushShape::Circle { radius } => (brush_center - pixel_position).norm() < radius, - BrushShape::Rectangle { width, length } => Rect::new( - brush_center.x - width * 0.5, - brush_center.y - length * 0.5, - width, - length, - ) - .contains(pixel_position), - } - } -} - -/// Paint mode of a brush. It defines operation that will be performed on the terrain. -#[derive(Clone, PartialEq, PartialOrd, Reflect, Debug)] -pub enum BrushMode { - /// Raise or lower the value - Raise { - /// An offset to change the value by - amount: f32, - }, - /// Flattens value of the terrain data - Flatten, - /// Assigns a particular value to anywhere the brush touches. - Assign { - /// Fixed value to paint into the data - value: f32, - }, - /// Reduce sharp changes in the data. - Smooth { - /// Determines the size of each pixel's neighborhood in terms of - /// distance from the pixel. - /// 0 means no smoothing at all. - /// 1 means taking the mean of the 3x3 square of pixels surrounding each smoothed pixel. - /// 2 means using a 5x5 square of pixels. And so on. - kernel_radius: u32, - }, -} - -uuid_provider!(BrushMode = "48ad4cac-05f3-485a-b2a3-66812713841f"); - -impl Default for BrushMode { - fn default() -> Self { - BrushMode::Raise { amount: 1.0 } - } -} - -/// Paint target of a brush. It defines the data that the brush will operate on. -#[derive(Copy, Default, Clone, Reflect, Debug, PartialEq, Eq)] -pub enum BrushTarget { - #[default] - /// Modifies the height map - HeightMap, - /// Draws on a given layer - LayerMask { - /// The number of the layer to modify - layer: usize, - }, - /// Modifies the terrain's holes - HoleMask, -} - -uuid_provider!(BrushTarget = "461c1be7-189e-44ee-b8fd-00b8fdbc668f"); - -/// Brush is used to modify terrain. It supports multiple shapes and modes. -#[derive(Clone, Reflect, Debug)] -pub struct Brush { - /// Shape of the brush. - pub shape: BrushShape, - /// Paint mode of the brush. - pub mode: BrushMode, - /// The data to modify with the brush - pub target: BrushTarget, - /// Transform that can modify the shape of the brush - pub transform: Matrix2, - /// The softness of the edges of the brush. - /// 0.0 means that the brush fades very gradually from opaque to transparent. - /// 1.0 means that the edges of the brush do not fade. - pub hardness: f32, - /// The transparency of the brush, allowing the values beneath the brushstroke to show through. - /// 0.0 means the brush is fully transparent and does not draw. - /// 1.0 means the brush is fully opaque. - pub alpha: f32, -} - -impl Default for Brush { - fn default() -> Self { - Self { - transform: Matrix2::identity(), - hardness: 0.0, - alpha: 1.0, - shape: Default::default(), - mode: Default::default(), - target: Default::default(), - } - } -} - -/// Verify that the brush operation is not so big that it could cause the editor to freeze. -/// The user can type in any size of brush they please, even disastrous sizes, and -/// this check prevents the editor from breaking. -fn within_size_limit(bounds: &Rect) -> bool { - let size = bounds.size; - let area = size.x * size.y; - let accepted = area <= BRUSH_PIXEL_SANITY_LIMIT; - if !accepted { - Log::warn(format!( - "Terrain brush operation dropped due to sanity limit: {}", - area - )) - } - accepted -} - -impl Brush { - /// Send the pixels for this brush to the brush thread. - /// - `position`: The position of the brush in texture pixels. - /// - `scale`: The size of each pixel in local 2D space. This is used - /// to convert the brush's radius from local 2D to pixels. - /// - `value`: The brush's value. The meaning of this number depends on the brush. - /// - `draw_pixel`: The function that will draw the pixels to the terrain. - pub fn stamp(&self, position: Vector2, scale: Vector2, mut draw_pixel: F) - where - F: FnMut(Vector2, f32), - { - let mut transform = self.transform; - let x_factor = scale.y / scale.x; - transform.m11 *= x_factor; - transform.m12 *= x_factor; - match self.shape { - BrushShape::Circle { radius } => { - let iter = StampPixels::new( - CircleRaster(radius / scale.y), - position, - self.hardness, - transform, - ); - if !within_size_limit(&iter.bounds()) { - return; - } - for BrushPixel { position, strength } in iter { - draw_pixel(position, strength); - } - } - BrushShape::Rectangle { width, length } => { - let iter = StampPixels::new( - RectRaster(width * 0.5 / scale.y, length * 0.5 / scale.y), - position, - self.hardness, - transform, - ); - if !within_size_limit(&iter.bounds()) { - return; - } - for BrushPixel { position, strength } in iter { - draw_pixel(position, strength); - } - } - } - } - /// Send the pixels for this brush to the brush thread. - /// - `start`: The position of the brush when it started the smear in texture pixels. - /// - `end`: The current position of the brush in texture pixels. - /// - `scale`: The size of each pixel in local 2D space. This is used - /// to convert the brush's radius from local 2D to pixels. - /// - `draw_pixel`: The function that will draw the pixels to the terrain. - pub fn smear( - &self, - start: Vector2, - end: Vector2, - scale: Vector2, - mut draw_pixel: F, - ) where - F: FnMut(Vector2, f32), - { - let mut transform = self.transform; - let x_factor = scale.y / scale.x; - transform.m11 *= x_factor; - transform.m12 *= x_factor; - match self.shape { - BrushShape::Circle { radius } => { - let iter = SmearPixels::new( - CircleRaster(radius / scale.y), - start, - end, - self.hardness, - transform, - ); - if !within_size_limit(&iter.bounds()) { - return; - } - for BrushPixel { position, strength } in iter { - draw_pixel(position, strength); - } - } - BrushShape::Rectangle { width, length } => { - let iter = SmearPixels::new( - RectRaster(width * 0.5 / scale.y, length * 0.5 / scale.y), - start, - end, - self.hardness, - transform, - ); - if !within_size_limit(&iter.bounds()) { - return; - } - for BrushPixel { position, strength } in iter { - draw_pixel(position, strength); - } - } - } - } -} - -/// A copy of a layer of data from a chunk. -/// It can be height data or mask data, since the type is erased. -/// The layer that this data represents must be remembered externally. -pub struct ChunkData { - /// The grid position of the original chunk. - pub grid_position: Vector2, - /// The size of the original chunk, to confirm that the chunk's size has not changed since the data was copied. - pub size: Vector2, - /// The type-erased data from either the height or one of the layers of the chunk. - pub content: Box<[u8]>, -} - -impl std::fmt::Debug for ChunkData { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("ChunkData") - .field("grid_position", &self.grid_position) - .field("content", &format!("[..](len: {})", &self.content.len())) - .finish() - } -} - -fn size_from_texture(texture: &ResourceDataRef<'_, Texture>) -> Vector2 { - match texture.kind() { - crate::resource::texture::TextureKind::Rectangle { width, height } => { - Vector2::new(width, height) - } - _ => unreachable!("Terrain texture was not rectangle."), - } -} - -impl ChunkData { - /// Extract the size from the given texture and return true if that size matches - /// the size required by this data. Log an error message and return false otherwise. - fn verify_texture_size(&self, texture: &ResourceDataRef<'_, Texture>) -> bool { - let size = size_from_texture(texture); - if size != self.size { - Log::err("Command swap failed due to texture size mismatch"); - false - } else { - true - } - } - /// Create a ChunkData for the given texture at the given position. - pub fn from_texture(grid_position: Vector2, texture: &TextureResource) -> Self { - let data_ref = texture.data_ref(); - let size = size_from_texture(&data_ref); - let data = Box::<[u8]>::from(data_ref.data()); - Self { - grid_position, - size, - content: data, - } - } - /// Swap the content of this data with the content of the given chunk's height map. - pub fn swap_height(&mut self, chunk: &mut Chunk) { - let mut data_ref = chunk.heightmap().data_ref(); - if !self.verify_texture_size(&data_ref) { - return; - } - let mut modify = data_ref.modify(); - for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) { - std::mem::swap(a, b); - } - } - /// Swap the content of this data with the content of the given chunk's hole mask. - pub fn swap_holes(&mut self, chunk: &mut Chunk) { - let Some(mut data_ref) = chunk.hole_mask.as_ref().map(|t| t.data_ref()) else { - return; - }; - if !self.verify_texture_size(&data_ref) { - return; - } - let mut modify = data_ref.modify(); - for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) { - std::mem::swap(a, b); - } - } - /// Swap the content of this data with the content of the given chunk's mask layer. - pub fn swap_layer_mask(&mut self, chunk: &mut Chunk, layer: usize) { - let mut data_ref = chunk.layer_masks[layer].data_ref(); - if !self.verify_texture_size(&data_ref) { - return; - } - let mut modify = data_ref.modify(); - for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) { - std::mem::swap(a, b); - } - } - /// Swap the height data of the a chunk from the list with the height data in this object. - /// The given list of chunks will be searched to find the chunk that matches `grid_position`. - pub fn swap_height_from_list(&mut self, chunks: &mut [Chunk]) { - for c in chunks { - if c.grid_position == self.grid_position { - self.swap_height(c); - break; - } - } - } - /// Swap the hole data of the a chunk from the list with the hole data in this object. - /// The given list of chunks will be searched to find the chunk that matches `grid_position`. - pub fn swap_holes_from_list(&mut self, chunks: &mut [Chunk]) { - for c in chunks { - if c.grid_position == self.grid_position { - self.swap_holes(c); - break; - } - } - } - /// Swap the layer mask data of a particular layer of a chunk from the list with the data in this object. - /// The given list of chunks will be searched to find the chunk that matches `grid_position`. - pub fn swap_layer_mask_from_list(&mut self, chunks: &mut [Chunk], layer: usize) { - for c in chunks { - if c.grid_position == self.grid_position { - self.swap_layer_mask(c, layer); - break; - } - } - } -} +// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +//! The brushstroke module contains tools for modifying terrain textures. +//! It uses a triple-buffer system to separate the UI mouse movements +//! from the update of the data within the actual textures. +//! 1. The first buffer is a [std::sync::mpsc::channel] that is used to send +//! messages to control a thread that processes brush strokes. +//! These messages are [BrushThreadMessage]. Some of the messages +//! are processed as soon as they are received, but [BrushThreadMessage::Pixel] +//! messages are sent to the next buffer. +//! 2. The pixel message buffer holds a limited number of pixel messages. +//! It serves to merge redundent pixel messages to spare the thread from +//! repeating work. It is expected that brush operations will paint multiple +//! times to the same pixel in quick succession. +//! Once the new value for a pixel has been calculated, the value is stored +//! in the third buffer. +//! 3. The [StrokeData] buffer stores every change that a particular brush stroke +//! has made to the texture. Because modifying a texture is a nontrivial operation, +//! modified pixels are allowed to accumulate to some quantity before the new pixel +//! values are actually written to the textures of the terrain. +//! [StrokeChunks] is used to keep track of which pixels are waiting to be written +//! to which terrain chunks. +use super::Chunk; +use crate::asset::ResourceDataRef; +use crate::core::{ + algebra::{Matrix2, Vector2}, + log::Log, + math::Rect, + pool::Handle, + reflect::prelude::*, +}; +use crate::fxhash::FxHashMap; +use crate::resource::texture::{Texture, TextureResource}; +use crate::scene::node::Node; +use fyrox_core::uuid_provider; +use std::collections::VecDeque; +use std::sync::mpsc::{Receiver, SendError, Sender}; + +pub mod brushraster; +use brushraster::*; +pub mod strokechunks; +use strokechunks::*; + +/// The number of pixel messages we can accept at once before we must start processing them. +/// Often later messages will cause earlier messages to be unnecessary, so it can be more efficient +/// to let some messages accumulate rather than process each message one-at-a-time. +const MESSAGE_BUFFER_SIZE: usize = 40; +/// The number of processed pixels we can hold before we must write the pixels to the targetted textures. +/// Modifying a texture is expensive, so it is important to do it in batches of multiple pixels. +const PIXEL_BUFFER_SIZE: usize = 40; +/// The maximum number of pixels that are allowed to be involved in a single step of a brushstroke. +/// This limit is arbitrarily chosen, but there should be some limit to prevent the editor +/// from freezing as a result of an excessively large brush. +const BRUSH_PIXEL_SANITY_LIMIT: i32 = 1000000; + +#[inline] +fn mask_raise(original: u8, amount: f32) -> u8 { + (original as f32 + amount * 255.0).clamp(0.0, 255.0) as u8 +} + +#[inline] +fn mask_lerp(original: u8, value: f32, t: f32) -> u8 { + let original = original as f32; + let value = value * 255.0; + (original * (1.0 - t) + value * t).clamp(0.0, 255.0) as u8 +} + +/// A message that can be sent to the terrain painting thread to control the painting. +#[derive(Debug, Clone)] +pub enum BrushThreadMessage { + /// Set the brush that will be used for future pixels and the textures that will be modified. + StartStroke(Brush, Handle, TerrainTextureData), + /// No futher pixels will be sent for the current stroke. + EndStroke, + /// Paint the given pixel as part of the current stroke. + Pixel(BrushPixelMessage), +} + +/// A message that can be sent to indicate that the pixel at the given coordinates +/// should be painted with the given alpha. +#[derive(Debug, Clone)] +pub struct BrushPixelMessage { + /// The coordinates of the pixel to paint. + pub position: Vector2, + /// The transparency of the brush, from 0.0 for transparent to 1.0 for opaque. + pub alpha: f32, + /// A value whose meaning depends on the brush. + /// For flatten brushes, this is the target height. + pub value: f32, +} + +/// A queue that stores pixels that are waiting to be drawn by the brush. +pub struct PixelMessageBuffer { + data: VecDeque, + max_size: usize, +} + +impl PixelMessageBuffer { + /// Create a new buffer with the given size. + #[inline] + pub fn new(max_size: usize) -> Self { + Self { + data: VecDeque::with_capacity(max_size), + max_size, + } + } + /// True if the buffer has reached its maximum size. + #[inline] + pub fn is_full(&self) -> bool { + self.max_size == self.data.len() + } + /// True if there is nothing to pop from the queue. + #[inline] + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + /// Remove the message from the front of the queue and return it, if the queue is not empty. + #[inline] + pub fn pop(&mut self) -> Option { + self.data.pop_front() + } + /// Push a message onto the back of the queue, or panic of the queue is full. + pub fn push(&mut self, message: BrushPixelMessage) { + assert!(self.data.len() < self.max_size); + if let Some(m) = self + .data + .iter_mut() + .find(|m| m.position == message.position) + { + if message.alpha > m.alpha { + m.alpha = message.alpha; + m.value = message.value; + } + } else { + self.data.push_back(message); + } + } +} + +/// Object to send to painting thread to control which textures are modified. +#[derive(Debug, Clone)] +pub struct TerrainTextureData { + /// The height and width of the texture in pixels. + pub chunk_size: Vector2, + /// The kind of texture. + pub kind: TerrainTextureKind, + /// The texture resources, organized by chunk grid position. + pub resources: FxHashMap, TextureResource>, +} + +/// Terrain textures come in multiple kinds. +/// Height textures contain f32 values for each vertex of the terrain. +/// Mask textures contain u8 values indicating transparency. +/// Coordinates are interpreted differently between the two kinds of texture +/// because the data in height textures overlaps with the data in neighboring chunks, +/// so the pixels along each edge are duplicated and must be kept in sync +/// so that the chunks do not disconnect. +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] +pub enum TerrainTextureKind { + #[default] + /// Height texture with f32 height values and overlapping edges between chunks. + Height, + /// Mask texture with u8 oppacity values. + Mask, +} + +/// Sender with methods for sending the messages which control a brush painting thread. +pub struct BrushSender(Sender); + +impl BrushSender { + /// Create a new BrushSender using the given Sender. + pub fn new(sender: Sender) -> Self { + Self(sender) + } + /// Begin a new stroke using the given brush. + pub fn start_stroke(&self, brush: Brush, node: Handle, data: TerrainTextureData) { + self.0 + .send(BrushThreadMessage::StartStroke(brush, node, data)) + .unwrap_or_else(on_send_failure); + } + /// End the current stroke. + pub fn end_stroke(&self) { + self.0 + .send(BrushThreadMessage::EndStroke) + .unwrap_or_else(on_send_failure); + } + /// Draw a pixel using the brush that was set in the most recent call to [BrushSender::start_stroke]. + #[inline] + pub fn draw_pixel(&self, position: Vector2, alpha: f32, value: f32) { + if alpha == 0.0 { + return; + } + self.0 + .send(BrushThreadMessage::Pixel(BrushPixelMessage { + position, + alpha, + value, + })) + .unwrap_or_else(on_send_failure); + } +} + +fn on_send_failure(error: SendError) { + Log::err(format!("A brush painting message was not sent. {error:?}")); +} + +/// Type for a callback that delivers the original data of textures that have been modified +/// by the brush so that changes might be undone. +pub type UndoChunkHandler = dyn FnMut(UndoData) + Send; + +/// A record of original data data for chunks that have been modified by a brushstroke. +pub struct UndoData { + /// The handle of the terrain being edited + pub node: Handle, + /// The data of the chunks as they were before the brushstroke. + pub chunks: Vec, + /// The kind of data within the terrain that is being edited. + pub target: BrushTarget, +} + +#[derive(Default)] +/// Data for an in-progress terrain painting operation +pub struct BrushStroke { + /// The brush that is currently being used. This determines how the terrain textures are edited. + brush: Brush, + /// The textures for the terrain that is currently being edited. + textures: FxHashMap, TextureResource>, + /// The node of the terrain being edted + node: Handle, + /// Callback to handle the saved original chunk data after each stroke. + /// This is called when [BrushThreadMessage::EndStroke] is received. + undo_chunk_handler: Option>, + /// A record of which pixels have been modified in each chunk since the last UpdateTextures. + /// This is cleared after [BrushThreadMessage::UpdateTextures] is received, + /// when the pixel data is transferred into the textures. + chunks: StrokeChunks, + /// Data copied from chunks that have been edited by the current brush stroke. + /// This preserves the textures as they were before the stroke began, so that + /// an undo command can be created at the end of the stroke. + undo_chunks: Vec, + /// A record of every pixel of the stroke, including the strength of the brush at that pixel, + /// the original value before the stroke began, and the current value. + height_pixels: StrokeData, + /// A record of every pixel of the stroke, including the strength of the brush at that pixel, + /// the original value before the stroke began, and the current value. + mask_pixels: StrokeData, +} + +/// Stores pixels that have been modified by a brush during a stroke. +/// It remembers the strength of the brush, the value of the painted pixel, +/// and the value of the original pixel before the brushstroke. +/// This should be cleared after each stroke using [StrokeData::clear]. +/// +/// `V` is the type of data stored in the pixel being edited. +#[derive(Debug, Default)] +pub struct StrokeData(FxHashMap, StrokeElement>); + +/// A single pixel data of a brush stroke +#[derive(Debug, Copy, Clone)] +pub struct StrokeElement { + /// The intensity of the brush stroke, with 0.0 indicating a pixel that brush has not touched + /// and 1.0 indicates a pixel fully covered by the brush. + pub strength: f32, + /// The value of the pixel before the stroke began. + pub original_value: V, + /// The current value of the pixel. + pub latest_value: V, +} + +impl BrushStroke { + /// Create a BrushStroke with the given handler for saving undo data for chunks. + pub fn with_chunk_handler(undo_chunk_handler: Box) -> Self { + Self { + undo_chunk_handler: Some(undo_chunk_handler), + ..Default::default() + } + } + /// The brush that this stroke is using. This is immutable access only, because + /// the brush's target may only be changed through [BrushStroke::start_stroke] or + /// [BrushStroke::accept_messages]. + /// + /// Mutable access to the brush's other properties is available through + /// [BrushStroke::shape], [BrushStroke::mode], [BrushStroke::hardness], + /// and [BrushStroke::alpha]. + pub fn brush(&self) -> &Brush { + &self.brush + } + /// Mutable access to the brush's shape + pub fn shape(&mut self) -> &mut BrushShape { + &mut self.brush.shape + } + /// Mutable access to the brush's mode + pub fn mode(&mut self) -> &mut BrushMode { + &mut self.brush.mode + } + /// Mutable access to the brush's hardness. The hardness controls how the edges + /// of the brush are blended with the original value of the texture. + pub fn hardness(&mut self) -> &mut f32 { + &mut self.brush.hardness + } + /// Mutable access to the brush's alpha. The alpha value controls how + /// the operation's result is blended with the original value of the texture. + pub fn alpha(&mut self) -> &mut f32 { + &mut self.brush.alpha + } + /// Insert a stamp of the brush at the given position with the given texture scale and the given value. + /// - `position`: The center of the stamp. + /// - `scale`: The size of each pixel in 2D local space. This is used to convert the brush's shape from local space to texture space. + /// - `value`: A value that is a parameter for the brush operation. + pub fn stamp(&mut self, position: Vector2, scale: Vector2, value: f32) { + let brush = self.brush.clone(); + brush.stamp(position, scale, |position, alpha| { + self.draw_pixel(BrushPixelMessage { + position, + alpha, + value, + }) + }); + } + /// Insert a smear of the brush at the given position with the given texture scale and the given value. + /// - `start`: The center of the smear's start. + /// - `end`: The center of the smear's end. + /// - `scale`: The size of each pixel in 2D local space. This is used to convert the brush's shape from local space to texture space. + /// - `value`: A value that is a parameter for the brush operation. + pub fn smear( + &mut self, + start: Vector2, + end: Vector2, + scale: Vector2, + value: f32, + ) { + let brush = self.brush.clone(); + brush.smear(start, end, scale, |position, alpha| { + self.draw_pixel(BrushPixelMessage { + position, + alpha, + value, + }) + }); + } + /// Prepare this object for a new brushstroke. + pub fn clear(&mut self) { + self.height_pixels.clear(); + self.mask_pixels.clear(); + self.chunks.clear(); + } + /// Access the data in the textures to find the value for the pixel at the given position. + pub fn data_pixel(&self, position: Vector2) -> Option + where + V: Clone, + { + // Determine which texture holds the data for the position. + let grid_pos = self.chunks.pixel_position_to_grid_position(position); + // Determine which pixel within the texture corresponds to the given position. + let origin = self.chunks.chunk_to_origin(grid_pos); + let p = position - origin; + // Access the texture and extract the data. + let texture = self.textures.get(&grid_pos)?; + let index = self.chunks.pixel_index(p); + let data = texture.data_ref(); + Some(data.data_of_type::().unwrap()[index].clone()) + } + /// Block on the given receiver until its messages are exhausted and perform the painting + /// operations according to the messages. + /// It does not return until the receiver's channel no longer has senders. + pub fn accept_messages(&mut self, receiver: Receiver) { + let mut message_buffer = PixelMessageBuffer::new(MESSAGE_BUFFER_SIZE); + loop { + // Collect all waiting messages, until the buffer is full. + while !message_buffer.is_full() { + if let Ok(message) = receiver.try_recv() { + // Act on the message, potentially adding it to the message buffer. + self.handle_message(message, &mut message_buffer); + } else { + break; + } + } + if let Some(pixel) = message_buffer.pop() { + // Perform the drawing operation for the current pixel message. + self.handle_pixel_message(pixel); + } else if self.chunks.count() > 0 { + // We have run out of pixels to process, so before we block to wait for more, + // write the currently processed pixels to the terrain textures. + self.flush(); + } else { + // If the message buffer is empty, we cannot proceed, so block until a message is available. + // Block until either a message arrives or the channel is closed. + if let Ok(message) = receiver.recv() { + // Act on the message, potentially adding it to the message buffer. + self.handle_message(message, &mut message_buffer); + } else { + // The message buffer is empty and the channel is closed, so we're finished. + // Flush pixels to the textures. + self.end_stroke(); + return; + } + } + } + } + fn handle_message( + &mut self, + message: BrushThreadMessage, + message_buffer: &mut PixelMessageBuffer, + ) { + match message { + BrushThreadMessage::StartStroke(brush, node, textures) => { + self.brush = brush; + self.node = node; + self.textures = textures.resources; + self.chunks.set_layout(textures.kind, textures.chunk_size); + } + BrushThreadMessage::EndStroke => { + // The stroke has ended, so finish processing all buffered pixel messages. + while let Some(p) = message_buffer.pop() { + self.handle_pixel_message(p); + } + // Apply buffered pixels to the terrain textures. + self.end_stroke(); + } + BrushThreadMessage::Pixel(pixel) => { + message_buffer.push(pixel); + } + } + } + /// -`brush`: The brush to paint with + /// -`node`: The handle of the terrain being modified. It is not used except to pass to `undo_chunk_handler`, + /// so it can be safely [Handle::NONE] if `undo_chunk_handler` is None, or if `undo_chunk_handler` is prepared for NONE. + /// -`textures`: Hash map of texture resources that this stroke will edit. + pub fn start_stroke(&mut self, brush: Brush, node: Handle, textures: TerrainTextureData) { + self.brush = brush; + self.node = node; + self.chunks.set_layout(textures.kind, textures.chunk_size); + self.textures = textures.resources; + } + /// Send the textures that have been touched by the brush to the undo handler, + /// then write the current changes to the textures and clear the stroke to prepare + /// for starting a new stroke. + pub fn end_stroke(&mut self) { + if let Some(handler) = &mut self.undo_chunk_handler { + // Copy the textures that are about to be modified so that the modifications can be undone. + self.chunks + .copy_texture_data(&self.textures, &mut self.undo_chunks); + // Send the saved textures to the handler so that an undo command might be created. + handler(UndoData { + node: self.node, + chunks: std::mem::take(&mut self.undo_chunks), + target: self.brush.target, + }); + } + // Flush pixels to the terrain textures + self.apply(); + self.clear(); + } + /// Insert a pixel with the given texture-space coordinates and strength. + pub fn draw_pixel(&mut self, pixel: BrushPixelMessage) { + let pixel = BrushPixelMessage { + alpha: 0.5 * (1.0 - (pixel.alpha * std::f32::consts::PI).cos()), + ..pixel + }; + let position = pixel.position; + match self.chunks.kind() { + TerrainTextureKind::Height => self.accept_pixel_height(pixel), + TerrainTextureKind::Mask => self.accept_pixel_mask(pixel), + } + self.chunks.write(position); + } + fn handle_pixel_message(&mut self, pixel: BrushPixelMessage) { + self.draw_pixel(pixel); + if self.chunks.count() >= PIXEL_BUFFER_SIZE { + self.flush(); + } + } + fn smooth_height( + &self, + position: Vector2, + kernel_radius: u32, + original: f32, + alpha: f32, + ) -> f32 { + let radius = kernel_radius as i32; + let diameter = kernel_radius * 2 + 1; + let area = (diameter * diameter - 1) as f32; + let mut total = 0.0; + for x in -radius..=radius { + for y in -radius..=radius { + if x == 0 && y == 0 { + continue; + } + let pos = position + Vector2::new(x, y); + let value = self + .height_pixels + .original_pixel_value(pos) + .copied() + .or_else(|| self.data_pixel(position)) + .unwrap_or_default(); + total += value; + } + } + let smoothed = total / area; + original * (1.0 - alpha) + smoothed * alpha + } + fn smooth_mask( + &self, + position: Vector2, + kernel_radius: u32, + original: u8, + alpha: f32, + ) -> u8 { + let radius = kernel_radius as i32; + let diameter = kernel_radius as u64 * 2 + 1; + let area = diameter * diameter - 1; + let mut total: u64 = 0; + for x in -radius..=radius { + for y in -radius..=radius { + if x == 0 && y == 0 { + continue; + } + let pos = position + Vector2::new(x, y); + let value = self + .mask_pixels + .original_pixel_value(pos) + .copied() + .or_else(|| self.data_pixel(position)) + .unwrap_or_default(); + total += value as u64; + } + } + let smoothed = total / area; + (original as f32 * (1.0 - alpha) + smoothed as f32 * alpha).clamp(0.0, 255.0) as u8 + } + fn accept_pixel_height(&mut self, pixel: BrushPixelMessage) { + let position = pixel.position; + let mut pixels = std::mem::take(&mut self.height_pixels); + let original = pixels.update_strength(position, pixel.alpha, || self.data_pixel(position)); + self.height_pixels = pixels; + let Some(original) = original else { + return; + }; + let alpha = self.brush.alpha * pixel.alpha; + let result: f32 = match self.brush.mode { + BrushMode::Raise { amount } => original + amount * alpha, + BrushMode::Flatten => original * (1.0 - alpha) + pixel.value * alpha, + BrushMode::Assign { value } => original * (1.0 - alpha) + value * alpha, + BrushMode::Smooth { kernel_radius } => { + self.smooth_height(position, kernel_radius, original, alpha) + } + }; + self.height_pixels.set_latest(position, result); + } + fn accept_pixel_mask(&mut self, pixel: BrushPixelMessage) { + let position = pixel.position; + let mut pixels = std::mem::take(&mut self.mask_pixels); + let original = pixels.update_strength(position, pixel.alpha, || self.data_pixel(position)); + self.mask_pixels = pixels; + let Some(original) = original else { + return; + }; + let alpha = self.brush.alpha * pixel.alpha; + let result: u8 = match self.brush.mode { + BrushMode::Raise { amount } => mask_raise(original, amount * alpha), + BrushMode::Flatten => mask_lerp(original, pixel.value, alpha), + BrushMode::Assign { value } => mask_lerp(original, value, alpha), + BrushMode::Smooth { kernel_radius } => { + self.smooth_mask(position, kernel_radius, original, alpha) + } + }; + self.mask_pixels.set_latest(position, result); + } + /// Update the texture resources to match the current state of this stroke. + pub fn flush(&mut self) { + // chunks stores the pixels that we have modified but not yet written to the textures. + // If we have an undo handler, we must inform the handler of the textures we are writing to + // because we are about to forget that we have written to them. + if self.undo_chunk_handler.is_some() { + // Copy the textures that are about to be modified so that the modifications can be undone. + self.chunks + .copy_texture_data(&self.textures, &mut self.undo_chunks); + } + // Do the actual texture modification. + self.apply(); + // Erase our memory of having modified these pixels, since they have already been finalized + // by writing them to the textures. + self.chunks.clear(); + } + fn apply(&self) { + match self.chunks.kind() { + TerrainTextureKind::Height => { + self.chunks.apply(&self.height_pixels, &self.textures); + } + TerrainTextureKind::Mask => { + self.chunks.apply(&self.mask_pixels, &self.textures); + } + } + } +} + +impl StrokeData { + /// Reset the brush stroke so it is ready to begin a new stroke. + #[inline] + pub fn clear(&mut self) { + self.0.clear() + } + /// For every pixel that is modified by the stroke, the original values + /// is stored as it was before the stroke began. + #[inline] + pub fn original_pixel_value(&self, position: Vector2) -> Option<&V> { + self.0.get(&position).map(|x| &x.original_value) + } + /// The updated pixel value based on whatever editing operation the stroke is performing. + #[inline] + pub fn latest_pixel_value(&self, position: Vector2) -> Option<&V> { + self.0.get(&position).map(|x| &x.latest_value) + } + /// Update the stroke with a new value at the given pixel position. + /// This must only be called after calling [StrokeData::update_strength] + /// to ensure that this stroke contains data for the position. + /// Otherwise this method may panic. + #[inline] + pub fn set_latest(&mut self, position: Vector2, value: V) { + if let Some(el) = self.0.get_mut(&position) { + el.latest_value = value; + } else { + panic!("Setting latest value of missing element"); + } + } + /// Stores or modifies the StrokeElement at the given position. + /// If the element is updated, return the original pixel value of the element. + /// - `position`: The position of the data to modify within the terrain. + /// - `strength`: The strength of the brush at the position, from 0.0 to 1.0. + /// The element is updated if the stored strength is less than the given strength. + /// If there is no stored strength, that is treated as a strength of 0.0. + /// - `pixel_value`: The current value of the data. + /// This may be stored in the StrokeData if no pixel value is currently recorded for the given position. + /// Otherwise, this value is ignored. + #[inline] + pub fn update_strength( + &mut self, + position: Vector2, + strength: f32, + pixel_value: F, + ) -> Option + where + V: Clone, + F: FnOnce() -> Option, + { + if strength == 0.0 { + None + } else if let Some(element) = self.0.get_mut(&position) { + if element.strength < strength { + element.strength = strength; + Some(element.original_value.clone()) + } else { + None + } + } else { + let value = pixel_value()?; + let element = StrokeElement { + strength, + latest_value: value.clone(), + original_value: value.clone(), + }; + self.0.insert(position, element); + Some(value) + } + } +} + +/// Shape of a brush. +#[derive(Copy, Clone, Reflect, Debug)] +pub enum BrushShape { + /// Circle with given radius. + Circle { + /// Radius of the circle. + radius: f32, + }, + /// Rectangle with given width and height. + Rectangle { + /// Width of the rectangle. + width: f32, + /// Length of the rectangle. + length: f32, + }, +} + +uuid_provider!(BrushShape = "a4dbfba0-077c-4658-9972-38384a8432f9"); + +impl Default for BrushShape { + fn default() -> Self { + BrushShape::Circle { radius: 1.0 } + } +} + +impl BrushShape { + /// Return true if the given point is within the shape when positioned at the given center point. + pub fn contains(&self, brush_center: Vector2, pixel_position: Vector2) -> bool { + match *self { + BrushShape::Circle { radius } => (brush_center - pixel_position).norm() < radius, + BrushShape::Rectangle { width, length } => Rect::new( + brush_center.x - width * 0.5, + brush_center.y - length * 0.5, + width, + length, + ) + .contains(pixel_position), + } + } +} + +/// Paint mode of a brush. It defines operation that will be performed on the terrain. +#[derive(Clone, PartialEq, PartialOrd, Reflect, Debug)] +pub enum BrushMode { + /// Raise or lower the value + Raise { + /// An offset to change the value by + amount: f32, + }, + /// Flattens value of the terrain data + Flatten, + /// Assigns a particular value to anywhere the brush touches. + Assign { + /// Fixed value to paint into the data + value: f32, + }, + /// Reduce sharp changes in the data. + Smooth { + /// Determines the size of each pixel's neighborhood in terms of + /// distance from the pixel. + /// 0 means no smoothing at all. + /// 1 means taking the mean of the 3x3 square of pixels surrounding each smoothed pixel. + /// 2 means using a 5x5 square of pixels. And so on. + kernel_radius: u32, + }, +} + +uuid_provider!(BrushMode = "48ad4cac-05f3-485a-b2a3-66812713841f"); + +impl Default for BrushMode { + fn default() -> Self { + BrushMode::Raise { amount: 1.0 } + } +} + +/// Paint target of a brush. It defines the data that the brush will operate on. +#[derive(Copy, Default, Clone, Reflect, Debug, PartialEq, Eq)] +pub enum BrushTarget { + #[default] + /// Modifies the height map + HeightMap, + /// Draws on a given layer + LayerMask { + /// The number of the layer to modify + layer: usize, + }, + /// Modifies the terrain's holes + HoleMask, +} + +uuid_provider!(BrushTarget = "461c1be7-189e-44ee-b8fd-00b8fdbc668f"); + +/// Brush is used to modify terrain. It supports multiple shapes and modes. +#[derive(Clone, Reflect, Debug)] +pub struct Brush { + /// Shape of the brush. + pub shape: BrushShape, + /// Paint mode of the brush. + pub mode: BrushMode, + /// The data to modify with the brush + pub target: BrushTarget, + /// Transform that can modify the shape of the brush + pub transform: Matrix2, + /// The softness of the edges of the brush. + /// 0.0 means that the brush fades very gradually from opaque to transparent. + /// 1.0 means that the edges of the brush do not fade. + pub hardness: f32, + /// The transparency of the brush, allowing the values beneath the brushstroke to show through. + /// 0.0 means the brush is fully transparent and does not draw. + /// 1.0 means the brush is fully opaque. + pub alpha: f32, +} + +impl Default for Brush { + fn default() -> Self { + Self { + transform: Matrix2::identity(), + hardness: 0.0, + alpha: 1.0, + shape: Default::default(), + mode: Default::default(), + target: Default::default(), + } + } +} + +/// Verify that the brush operation is not so big that it could cause the editor to freeze. +/// The user can type in any size of brush they please, even disastrous sizes, and +/// this check prevents the editor from breaking. +fn within_size_limit(bounds: &Rect) -> bool { + let size = bounds.size; + let area = size.x * size.y; + let accepted = area <= BRUSH_PIXEL_SANITY_LIMIT; + if !accepted { + Log::warn(format!( + "Terrain brush operation dropped due to sanity limit: {area}" + )) + } + accepted +} + +impl Brush { + /// Send the pixels for this brush to the brush thread. + /// - `position`: The position of the brush in texture pixels. + /// - `scale`: The size of each pixel in local 2D space. This is used + /// to convert the brush's radius from local 2D to pixels. + /// - `value`: The brush's value. The meaning of this number depends on the brush. + /// - `draw_pixel`: The function that will draw the pixels to the terrain. + pub fn stamp(&self, position: Vector2, scale: Vector2, mut draw_pixel: F) + where + F: FnMut(Vector2, f32), + { + let mut transform = self.transform; + let x_factor = scale.y / scale.x; + transform.m11 *= x_factor; + transform.m12 *= x_factor; + match self.shape { + BrushShape::Circle { radius } => { + let iter = StampPixels::new( + CircleRaster(radius / scale.y), + position, + self.hardness, + transform, + ); + if !within_size_limit(&iter.bounds()) { + return; + } + for BrushPixel { position, strength } in iter { + draw_pixel(position, strength); + } + } + BrushShape::Rectangle { width, length } => { + let iter = StampPixels::new( + RectRaster(width * 0.5 / scale.y, length * 0.5 / scale.y), + position, + self.hardness, + transform, + ); + if !within_size_limit(&iter.bounds()) { + return; + } + for BrushPixel { position, strength } in iter { + draw_pixel(position, strength); + } + } + } + } + /// Send the pixels for this brush to the brush thread. + /// - `start`: The position of the brush when it started the smear in texture pixels. + /// - `end`: The current position of the brush in texture pixels. + /// - `scale`: The size of each pixel in local 2D space. This is used + /// to convert the brush's radius from local 2D to pixels. + /// - `draw_pixel`: The function that will draw the pixels to the terrain. + pub fn smear( + &self, + start: Vector2, + end: Vector2, + scale: Vector2, + mut draw_pixel: F, + ) where + F: FnMut(Vector2, f32), + { + let mut transform = self.transform; + let x_factor = scale.y / scale.x; + transform.m11 *= x_factor; + transform.m12 *= x_factor; + match self.shape { + BrushShape::Circle { radius } => { + let iter = SmearPixels::new( + CircleRaster(radius / scale.y), + start, + end, + self.hardness, + transform, + ); + if !within_size_limit(&iter.bounds()) { + return; + } + for BrushPixel { position, strength } in iter { + draw_pixel(position, strength); + } + } + BrushShape::Rectangle { width, length } => { + let iter = SmearPixels::new( + RectRaster(width * 0.5 / scale.y, length * 0.5 / scale.y), + start, + end, + self.hardness, + transform, + ); + if !within_size_limit(&iter.bounds()) { + return; + } + for BrushPixel { position, strength } in iter { + draw_pixel(position, strength); + } + } + } + } +} + +/// A copy of a layer of data from a chunk. +/// It can be height data or mask data, since the type is erased. +/// The layer that this data represents must be remembered externally. +pub struct ChunkData { + /// The grid position of the original chunk. + pub grid_position: Vector2, + /// The size of the original chunk, to confirm that the chunk's size has not changed since the data was copied. + pub size: Vector2, + /// The type-erased data from either the height or one of the layers of the chunk. + pub content: Box<[u8]>, +} + +impl std::fmt::Debug for ChunkData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ChunkData") + .field("grid_position", &self.grid_position) + .field("content", &format!("[..](len: {})", &self.content.len())) + .finish() + } +} + +fn size_from_texture(texture: &ResourceDataRef<'_, Texture>) -> Vector2 { + match texture.kind() { + crate::resource::texture::TextureKind::Rectangle { width, height } => { + Vector2::new(width, height) + } + _ => unreachable!("Terrain texture was not rectangle."), + } +} + +impl ChunkData { + /// Extract the size from the given texture and return true if that size matches + /// the size required by this data. Log an error message and return false otherwise. + fn verify_texture_size(&self, texture: &ResourceDataRef<'_, Texture>) -> bool { + let size = size_from_texture(texture); + if size != self.size { + Log::err("Command swap failed due to texture size mismatch"); + false + } else { + true + } + } + /// Create a ChunkData for the given texture at the given position. + pub fn from_texture(grid_position: Vector2, texture: &TextureResource) -> Self { + let data_ref = texture.data_ref(); + let size = size_from_texture(&data_ref); + let data = Box::<[u8]>::from(data_ref.data()); + Self { + grid_position, + size, + content: data, + } + } + /// Swap the content of this data with the content of the given chunk's height map. + pub fn swap_height(&mut self, chunk: &mut Chunk) { + let mut data_ref = chunk.heightmap().data_ref(); + if !self.verify_texture_size(&data_ref) { + return; + } + let mut modify = data_ref.modify(); + for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) { + std::mem::swap(a, b); + } + } + /// Swap the content of this data with the content of the given chunk's hole mask. + pub fn swap_holes(&mut self, chunk: &mut Chunk) { + let Some(mut data_ref) = chunk.hole_mask.as_ref().map(|t| t.data_ref()) else { + return; + }; + if !self.verify_texture_size(&data_ref) { + return; + } + let mut modify = data_ref.modify(); + for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) { + std::mem::swap(a, b); + } + } + /// Swap the content of this data with the content of the given chunk's mask layer. + pub fn swap_layer_mask(&mut self, chunk: &mut Chunk, layer: usize) { + let mut data_ref = chunk.layer_masks[layer].data_ref(); + if !self.verify_texture_size(&data_ref) { + return; + } + let mut modify = data_ref.modify(); + for (a, b) in modify.data_mut().iter_mut().zip(self.content.iter_mut()) { + std::mem::swap(a, b); + } + } + /// Swap the height data of the a chunk from the list with the height data in this object. + /// The given list of chunks will be searched to find the chunk that matches `grid_position`. + pub fn swap_height_from_list(&mut self, chunks: &mut [Chunk]) { + for c in chunks { + if c.grid_position == self.grid_position { + self.swap_height(c); + break; + } + } + } + /// Swap the hole data of the a chunk from the list with the hole data in this object. + /// The given list of chunks will be searched to find the chunk that matches `grid_position`. + pub fn swap_holes_from_list(&mut self, chunks: &mut [Chunk]) { + for c in chunks { + if c.grid_position == self.grid_position { + self.swap_holes(c); + break; + } + } + } + /// Swap the layer mask data of a particular layer of a chunk from the list with the data in this object. + /// The given list of chunks will be searched to find the chunk that matches `grid_position`. + pub fn swap_layer_mask_from_list(&mut self, chunks: &mut [Chunk], layer: usize) { + for c in chunks { + if c.grid_position == self.grid_position { + self.swap_layer_mask(c, layer); + break; + } + } + } +} diff --git a/fyrox-impl/src/scene/terrain/geometry.rs b/fyrox-impl/src/scene/terrain/geometry.rs index 1a8cb2bda..8665598fb 100644 --- a/fyrox-impl/src/scene/terrain/geometry.rs +++ b/fyrox-impl/src/scene/terrain/geometry.rs @@ -148,7 +148,7 @@ mod tests { let ElementRange::Specific { count, .. } = q else { panic!("Quadrant is full.") }; - assert_eq!(count, 2, "Quadrant: {}", i); + assert_eq!(count, 2, "Quadrant: {i}"); } } #[test] @@ -159,7 +159,7 @@ mod tests { let ElementRange::Specific { count, .. } = q else { panic!("Quadrant is full.") }; - assert_eq!(count, c, "Quadrant: {}", i); + assert_eq!(count, c, "Quadrant: {i}"); } } #[test] @@ -169,7 +169,7 @@ mod tests { let ElementRange::Specific { count, .. } = q else { panic!("Quadrant is full.") }; - assert_eq!(count, 8, "Quadrant: {}", i); + assert_eq!(count, 8, "Quadrant: {i}"); } } #[test] diff --git a/fyrox-impl/src/scene/terrain/mod.rs b/fyrox-impl/src/scene/terrain/mod.rs index 154bc53fe..f59f383d5 100644 --- a/fyrox-impl/src/scene/terrain/mod.rs +++ b/fyrox-impl/src/scene/terrain/mod.rs @@ -1266,7 +1266,7 @@ impl Visit for Terrain { if region.is_reading() { self.geometry = TerrainGeometry::new(*self.block_size); if version < 2 { - Log::info(format!("Updating terrain to version: {}", VERSION)); + Log::info(format!("Updating terrain to version: {VERSION}")); *self.height_map_size = self.height_map_size.map(|x| x + 2); for c in self.chunks.iter() { if c.height_map_size() != self.height_map_size() { diff --git a/fyrox-impl/src/utils/astar.rs b/fyrox-impl/src/utils/astar.rs index 719e937ab..7cb76e32e 100644 --- a/fyrox-impl/src/utils/astar.rs +++ b/fyrox-impl/src/utils/astar.rs @@ -756,7 +756,7 @@ mod test { println!(); for size in [10, 40, 100, 500] { - println!("benchmarking grid size of: {}^2", size); + println!("benchmarking grid size of: {size}^2"); let setup_start_time = Instant::now(); let mut pathfinder = Graph::new(); diff --git a/fyrox-impl/src/utils/lightmap.rs b/fyrox-impl/src/utils/lightmap.rs index f7589dfe7..2f69176db 100644 --- a/fyrox-impl/src/utils/lightmap.rs +++ b/fyrox-impl/src/utils/lightmap.rs @@ -760,7 +760,7 @@ mod test { for entry_set in lightmap.map.values() { for entry in entry_set { let mut data = entry.texture.as_ref().unwrap().data_ref(); - data.save(Path::new(&format!("{}.png", counter))).unwrap(); + data.save(Path::new(&format!("{counter}.png"))).unwrap(); counter += 1; } } diff --git a/fyrox-sound/src/error.rs b/fyrox-sound/src/error.rs index 2a6ae34db..7584df092 100644 --- a/fyrox-sound/src/error.rs +++ b/fyrox-sound/src/error.rs @@ -96,21 +96,21 @@ impl From for SoundError { impl Display for SoundError { fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { match self { - SoundError::Io(io) => write!(f, "io error: {}", io), + SoundError::Io(io) => write!(f, "io error: {io}"), SoundError::NoBackend => write!(f, "no backend implemented for current platform"), SoundError::FailedToInitializeDevice(reason) => { - write!(f, "failed to initialize device. reason: {}", reason) + write!(f, "failed to initialize device. reason: {reason}") } SoundError::InvalidHeader => write!(f, "invalid header of sound file"), SoundError::UnsupportedFormat => write!(f, "unsupported format of sound file"), SoundError::PoisonedMutex => write!(f, "attempt to use poisoned mutex"), SoundError::MathError(reason) => { - write!(f, "math error has occurred. reason: {}", reason) + write!(f, "math error has occurred. reason: {reason}") } SoundError::StreamingBufferAlreadyInUse => { write!(f, "streaming buffer in already in use") } - SoundError::DecoderError(de) => write!(f, "internal decoder error: {:?}", de), + SoundError::DecoderError(de) => write!(f, "internal decoder error: {de:?}"), SoundError::BufferFailedToLoad => write!(f, "a buffer failed to load"), SoundError::BufferIsNotLoaded => write!(f, "a buffer is not loaded yet"), } diff --git a/fyrox-ui/src/curve/mod.rs b/fyrox-ui/src/curve/mod.rs index f28f42441..feb4d95e4 100644 --- a/fyrox-ui/src/curve/mod.rs +++ b/fyrox-ui/src/curve/mod.rs @@ -1394,7 +1394,7 @@ impl CurveEditor { if self.show_y_values { for y in self.curve_transform.y_step_iter(self.grid_size.y) { - text.set_text(format!("{:.1}", y)).build(); + text.set_text(format!("{y:.1}")).build(); ctx.draw_text( self.clip_bounds(), self.point_to_screen_space(Vector2::new(local_left_bottom_n.x, y)), @@ -1405,7 +1405,7 @@ impl CurveEditor { if self.show_x_values { for x in self.curve_transform.x_step_iter(self.grid_size.x) { - text.set_text(format!("{:.1}", x)).build(); + text.set_text(format!("{x:.1}")).build(); ctx.draw_text( self.clip_bounds(), self.point_to_screen_space(Vector2::new(x, local_left_bottom_n.y)), diff --git a/fyrox-ui/src/inspector/mod.rs b/fyrox-ui/src/inspector/mod.rs index 765783e9d..65f836eb7 100644 --- a/fyrox-ui/src/inspector/mod.rs +++ b/fyrox-ui/src/inspector/mod.rs @@ -338,7 +338,7 @@ impl PropertyChanged { FieldKind::Inspectable(inspectable) => { path += format!("[{}].{}", index, inspectable.path()).as_ref(); } - _ => path += format!("[{}]", index).as_ref(), + _ => path += format!("[{index}]").as_ref(), } } } @@ -651,7 +651,7 @@ fn make_expander_check_box( let description = if property_description.is_empty() { property_name.to_string() } else { - format!("{}\n\n{}", property_name, property_description) + format!("{property_name}\n\n{property_description}") }; let handle = CheckBoxBuilder::new( @@ -826,7 +826,7 @@ impl InspectorContext { object.fields(&mut |fields| { for field in fields { fields_text.push(if generate_property_string_values { - format!("{:?}", field) + format!("{field:?}") } else { Default::default() }) @@ -903,8 +903,7 @@ impl InspectorContext { .with_vertical_text_alignment(VerticalAlignment::Center) .with_text(format!( "Unable to create property \ - editor instance: Reason {:?}", - e + editor instance: Reason {e:?}" )) .build(ctx), &description, diff --git a/fyrox-ui/src/lib.rs b/fyrox-ui/src/lib.rs index 455c667d7..75992a876 100644 --- a/fyrox-ui/src/lib.rs +++ b/fyrox-ui/src/lib.rs @@ -512,7 +512,7 @@ impl NodeStatistics { let prev_count = prev_stats.count_of(type_name); let delta = count - prev_count; if delta != 0 || show_unchanged { - println!("{}: \x1b[93m{}\x1b[0m", type_name, delta); + println!("{type_name}: \x1b[93m{delta}\x1b[0m"); } } } @@ -523,7 +523,7 @@ impl NodeStatistics { let count = self.count_of(type_name); let prev_count = prev_stats.count_of(type_name); if count - prev_count != 0 { - println!("{}: \x1b[93m{}\x1b[0m", type_name, count); + println!("{type_name}: \x1b[93m{count}\x1b[0m"); } } } @@ -3015,8 +3015,7 @@ impl UserInterface { let used_resources_count = used_resources.len(); Log::info(format!( - "UserInterface - {} resources collected. Waiting them to load...", - used_resources_count + "UserInterface - {used_resources_count} resources collected. Waiting them to load..." )); // Wait everything. diff --git a/fyrox-ui/src/node/container.rs b/fyrox-ui/src/node/container.rs index 339b633c2..609a6ac0d 100644 --- a/fyrox-ui/src/node/container.rs +++ b/fyrox-ui/src/node/container.rs @@ -50,7 +50,7 @@ fn read_widget(name: &str, visitor: &mut Visitor) -> Result let mut widget = serialization_context .try_create(&id) - .ok_or_else(|| panic!("Unknown widget type uuid {}!", id)) + .ok_or_else(|| panic!("Unknown widget type uuid {id}!")) .unwrap(); widget.visit("WidgetData", &mut region)?; diff --git a/project-manager/src/main.rs b/project-manager/src/main.rs index 86eaafc9a..3181b8372 100644 --- a/project-manager/src/main.rs +++ b/project-manager/src/main.rs @@ -150,7 +150,7 @@ fn main() { if let Err(e) = engine.set_frame_size(size.into()) { Log::writeln( MessageKind::Error, - format!("Unable to set frame size: {:?}", e), + format!("Unable to set frame size: {e:?}"), ); } } @@ -452,7 +452,7 @@ impl ProjectManager { self.build_window = Some(build_window); } - Err(e) => Log::err(format!("Failed to start the editor: {:?}", e)), + Err(e) => Log::err(format!("Failed to start the editor: {e:?}")), } } else if button == self.run { let mut new_process = std::process::Command::new("cargo"); @@ -469,7 +469,7 @@ impl ProjectManager { self.build_window = Some(build_window); } - Err(e) => Log::err(format!("Failed to start the game: {:?}", e)), + Err(e) => Log::err(format!("Failed to start the game: {e:?}")), } } else if button == self.delete { if let Some(dir) = project.manifest_path.parent() { diff --git a/template-core/src/lib.rs b/template-core/src/lib.rs index 6ea744942..497e0325a 100644 --- a/template-core/src/lib.rs +++ b/template-core/src/lib.rs @@ -75,8 +75,7 @@ impl Display for NameErrors { match self { Self::CargoReserved(name) => write!( f, - "The project name cannot be `{}` due to cargo's reserved keywords", - name + "The project name cannot be `{name}` due to cargo's reserved keywords" ), Self::Hyphen => write!(f, "The project name cannot contain `-`"), Self::StartsWithNumber => write!(f, "The project name cannot start with a number"), @@ -528,8 +527,7 @@ crate-type = ["cdylib"] [dependencies] fyrox = {{ workspace = true }} -{} = {{ path = "../game" }}"#, - name, +{name} = {{ path = "../game" }}"#, ), )?; @@ -636,7 +634,7 @@ fn init_data(base_path: &Path, style: &str) -> Result<(), String> { match style { "2d" => write_file_binary(scene_path, include_bytes!("2d.rgs")), "3d" => write_file_binary(scene_path, include_bytes!("3d.rgs")), - _ => Err(format!("Unknown style: {}. Use either `2d` or `3d`", style)), + _ => Err(format!("Unknown style: {style}. Use either `2d` or `3d`")), } } @@ -652,7 +650,7 @@ pub fn init_script(root_path: &Path, raw_name: &str) -> Result<(), String> { let file_name = base_path.join(script_file_stem.clone() + ".rs"); if file_name.exists() { - return Err(format!("Script {} already exists!", script_name)); + return Err(format!("Script {script_name} already exists!")); } let script_uuid = Uuid::new_v4().to_string(); @@ -667,13 +665,13 @@ use fyrox::{{ }}; #[derive(Visit, Reflect, Default, Debug, Clone, TypeUuidProvider, ComponentProvider)] -#[type_uuid(id = "{id}")] +#[type_uuid(id = "{script_uuid}")] #[visit(optional)] -pub struct {name} {{ +pub struct {script_name} {{ // Add fields here. }} -impl ScriptTrait for {name} {{ +impl ScriptTrait for {script_name} {{ fn on_init(&mut self, context: &mut ScriptContext) {{ // Put initialization logic here. }} @@ -695,9 +693,7 @@ impl ScriptTrait for {name} {{ // Put object logic here. }} }} - "#, - name = script_name, - id = script_uuid + "# ), ) } @@ -713,7 +709,7 @@ pub fn init_project( let name = match name { Ok(s) => s, Err(name_error) => { - println!("{}", name_error); + println!("{name_error}"); return Err(name_error.to_string()); } }; diff --git a/template/src/main.rs b/template/src/main.rs index 8b22e86d4..57eaebabd 100644 --- a/template/src/main.rs +++ b/template/src/main.rs @@ -79,11 +79,8 @@ fn main() { fyrox_template_core::init_project(Path::new("./"), &name, &style, &vcs, overwrite) .unwrap(); - println!("Project {} was generated successfully!", name); - println!( - "Navigate to {} directory and use one of the following commands:", - name - ); + println!("Project {name} was generated successfully!"); + println!("Navigate to {name} directory and use one of the following commands:"); println!("\tRun the Editor: cargo run --package editor --release"); println!("\tRun the Executor: cargo run --package executor --release"); println!( @@ -103,7 +100,7 @@ fn main() { Commands::Upgrade { version, local } => { fyrox_template_core::upgrade_project(Path::new("./"), &version, local).unwrap(); - println!("Fyrox version was successfully set to '{}'!", version); + println!("Fyrox version was successfully set to '{version}'!"); } } }